Тип ключа индекса объекта в Typescript
Я определил свой общий тип как
interface IDictionary<TValue> {
[key: string|number]: TValue;
}
но жаловаться TSLint по. Как я должен определить тип индекса объекта, который может иметь либо как ключ? Я попробовал и это, но безуспешно.
interface IDictionary<TKey, TValue> {
[key: TKey]: TValue;
}
interface IDictionary<TKey extends string|number, TValue> {
[key: TKey]: TValue;
}
type IndexKey = string | number;
interface IDictionary<TValue> {
[key: IndexKey]: TValue;
}
interface IDictionary<TKey extends IndexKey, TValue> {
[key: TKey]: TValue;
}
ни одна из вышеперечисленных работ.
так как же тогда?
3 ответов
вы можете достичь этого, просто используя
IDictionary<TValue> { [key: string]: TValue }
поскольку числовые значения будут автоматически преобразованы в строку.
пример использования:
interface IDictionary<TValue> {
[id: string]: TValue;
}
class Test {
private dictionary: IDictionary<string>;
constructor() {
this.dictionary = {}
this.dictionary[10] = "numeric-index";
this.dictionary["11"] = "string-index"
console.log(this.dictionary[10], this.dictionary["11"]);
}
}
// result => "numeric-index string-index"
в javascript ключи объекта могут быть только строками (и в es6
символов).
Если вы передадите число, оно преобразуется в строку:
let o = {};
o[3] = "three";
console.log(Object.keys(o)); // ["3"]
как вы можете видеть, вы всегда получаете { [key: string]: TValue; }
.
Typescript позволяет определить карту, как так с number
S как кнопки:
type Dict = { [key: number]: string };
и компилятор проверит, что при назначении значений вы всегда передаете число в качестве ключа, но во время выполнения ключи в объекте будут веревка.
, так что вы можете иметь { [key: number]: string }
или { [key: string]: string }
но не союз string | number
из-за следующего:
let d = {} as IDictionary<string>;
d[3] = "1st three";
d["3"] = "2nd three";
вы можете ожидать d
здесь две разных записи, но на самом деле есть только одна.
что вы можете сделать, это использовать Map
:
let m = new Map<number|string, string>();
m.set(3, "1st three");
m.set("3", "2nd three");
у вас будет две разные записи.несмотря на то, что ключи объектов всегда являются строками под капотом, и ввод индексаторов, поскольку строки охватывают числа, иногда вы хотите, чтобы функция знала о ключах объектов, передаваемых в нее. Рассмотрим эту функцию отображения, которая работает как Array.map
но с предметами:
function map<T>(obj: Object, callback: (key: string, value: any) => T): T[] {
// ...
}
key
ограничивается быть string
, и значение полностью нетипизировано. Возможно, в 9 из 10 раз, но мы можем сделать лучше. Скажем, мы хотели сделать что-то глупое это:
const obj: {[key: number]: string} = { 1: "hello", 2: "world", 3: "foo", 4: "bar" };
map(obj, (key, value) => `${key / 2} ${value}`);
// error: The left-hand side of an arithmetic operation must be of type 'any', 'number' or an enum type.
мы не можем выполнять какие-либо арифметические операции над ключом без первого приведения его к числу (помните:"3" / 2
действителен в JS и разрешается в number
). Мы можем обойти это с помощью немного хитрого ввода на нашей функции карты:
function map<S, T>(obj: S, callback: (key: keyof S, value: S[keyof S]) => T): T[] {
return Object.keys(obj).map(key => callback(key as any, (obj as any)[key]));
}
здесь мы используем универсальный S
для ввода нашего объекта и поиска типов ключей и значений непосредственно из этого. Если ваш объект набирается с использованием общих индексаторов и значений, keyof S
и S[keyof S]
рассосется к постоянным типам. Если вы передаете объект со свойствами, эксплицировать, keyof S
будут ограничены именами свойств и S[keyof S]
будет ограничено типами значений свойств.