Typescript中的对象索引键类型

时间:2017-01-26 09:32:48

标签: typescript typescript-generics

我将我的通用类型定义为

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 个答案:

答案 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 SS[keyof S]将解析为常量类型。如果传入具有explicate属性的对象,keyof S将仅限于属性名称,S[keyof S]将被限制为属性值类型。