我将我的通用类型定义为
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;
}
以上所有工作都没有。
答案 0 :(得分:24)
你可以通过使用一个来实现
IDictionary<TValue> { [key: string]: TValue }
因为数值会自动转换为字符串。
以下是一个使用示例:
interface IDictionary<TValue> {
[id: string]: TValue;
}
class Test {
private dictionary: IDictionary<string>;
constructor() {
this.dictionary = {}
this.dictionary[9] = "numeric-index";
this.dictionary["10"] = "string-index"
console.log(this.dictionary["9"], this.dictionary[10]);
}
}
// result => "numeric-index string-index"
正如您所见,字符串和数字索引可以互换。
答案 1 :(得分:17)
在javascript中,对象的键只能是字符串(以及es6
符号)
如果你传递一个数字,它会转换成一个字符串:
let o = {};
o[3] = "three";
console.log(Object.keys(o)); // ["3"]
如你所见,你总是得到{ [key: string]: TValue; }
。
Typescript允许您使用number
作为键来定义这样的地图:
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");
在这里,您将有两个不同的条目。
答案 2 :(得分:0)
即使对象键总是字符串下的字符串,并且键入索引器作为字符串覆盖数字,有时您希望函数知道传递给它的对象的键。考虑这个映射函数,它的作用类似于Array.map
但是对象:
function map<T>(obj: Object, callback: (key: string, value: any) => T): T[] {
// ...
}
key
仅限于string
,且值完全无类型。十分之九可能很好,但我们可以做得更好。让我们说我们想做一些像这样愚蠢的事情:
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]
将解析为常量类型。如果传入具有explicate属性的对象,keyof S
将仅限于属性名称,S[keyof S]
将被限制为属性值类型。