Typescript 中的对象索引键类型
Object index key type in 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;
}
None 以上作品。
那怎么办?
在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");
这里会有两个不同的条目。
您只需使用
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"
如您所见,字符串和数字索引可以互换。
尽管对象键始终是引擎盖下的字符串,并且将索引器键入为字符串覆盖数字,但有时您希望函数知道传递给它的对象的键。考虑这个映射函数,它的工作方式类似于 Array.map
但对象为:
function map<T>(obj: Object, callback: (key: string, value: any) => T): T[] {
// ...
}
key
被限制为 string
,并且值完全没有类型。可能 10 次中有 9 次都很好,但我们可以做得更好。假设我们想做这样的傻事:
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.
我们不能对 key 执行任何算术运算,除非先将其转换为数字(请记住:"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]
将被限制为 属性 值类型。
我将泛型类型定义为
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;
}
None 以上作品。
那怎么办?
在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");
这里会有两个不同的条目。
您只需使用
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"
如您所见,字符串和数字索引可以互换。
尽管对象键始终是引擎盖下的字符串,并且将索引器键入为字符串覆盖数字,但有时您希望函数知道传递给它的对象的键。考虑这个映射函数,它的工作方式类似于 Array.map
但对象为:
function map<T>(obj: Object, callback: (key: string, value: any) => T): T[] {
// ...
}
key
被限制为 string
,并且值完全没有类型。可能 10 次中有 9 次都很好,但我们可以做得更好。假设我们想做这样的傻事:
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.
我们不能对 key 执行任何算术运算,除非先将其转换为数字(请记住:"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]
将被限制为 属性 值类型。