Typescript 中的对象索引键类型

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/41870411/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-09 07:33:46  来源:igfitidea点击:

Object index key type in Typescript

typescripttypescript-generics

提问by Robert Koritnik

I defined my generic type as

我将泛型类型定义为

interface IDictionary<TValue> {
    [key: string|number]: TValue;
}

But TSLint's complaining. How am I supposed to define an object index type that can have either as key? I tried these as well but no luck.

但是 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 of the above work.

以上都不起作用。

So how then?

那怎么办呢?

回答by Nickeat

You can achieve that just by using a IDictionary<TValue> { [key: string]: TValue }since numeric values will be automatically converted to string.

您可以通过使用 a 来实现这一点, IDictionary<TValue> { [key: string]: TValue }因为数值将自动转换为字符串。

Here is an example of usage:

下面是一个使用示例:

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"

As you can see string and numeric indices are interchangeable.

如您所见,字符串索引和数字索引是可以互换的。

回答by Nitzan Tomer

In javascript the keys of object can only be strings (and in es6symbols as well).
If you pass a number it gets converted into a string:

在javascript中,对象的键只能是字符串(也可以是es6符号)。
如果你传递一个数字,它会被转换成一个字符串:

let o = {};
o[3] = "three";
console.log(Object.keys(o)); // ["3"]

As you can see, you always get { [key: string]: TValue; }.

如您所见,您总是会得到{ [key: string]: TValue; }.

Typescript lets you define a map like so with numbers as keys:

Typescript 允许您像这样定义一个以numbers 作为键的映射:

type Dict = { [key: number]: string };

And the compiler will check that when assigning values you always pass a number as a key, but in runtime the keys in the object will be strings.

并且编译器会检查在赋值时总是传递一个数字作为键,但在运行时对象中的键将是字符串。

So you can either have { [key: number]: string }or { [key: string]: string }but not a union of string | numberbecause of the following:

因此,由于以下原因,您可以有{ [key: number]: string }{ [key: string]: string }没有联合string | number

let d = {} as IDictionary<string>;
d[3] = "1st three";
d["3"] = "2nd three";

You might expect dto have two different entries here, but in fact there's just one.

您可能希望d这里有两个不同的条目,但实际上只有一个。

What you can do, is use a Map:

你可以做的是使用一个Map

let m = new Map<number|string, string>();
m.set(3, "1st three");
m.set("3", "2nd three");

Here you will have two different entries.

在这里,您将有两个不同的条目。

回答by Sandy Gifford

Even though object keys are always strings under the hood, and typing indexers as strings covers numbers, sometimes you want a function to be aware of the keys of objects being passed into it. Consider this mapping function which works like Array.mapbut with objects:

尽管对象键总是底层的字符串,并且键入索引器作为字符串覆盖数字,但有时您希望函数知道传递给它的对象的键。考虑这个映射函数,它的工作原理类似于Array.map对象:

function map<T>(obj: Object, callback: (key: string, value: any) => T): T[] {
    // ...
}

keyis restricted to being a string, and value is entirely untyped. Probably fine 9 out of 10 times, but we can do better. Let's say we wanted to do something silly like this:

key被限制为 a string,并且 value 是完全无类型的。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.

We can't perform any arithmetic operations on key without first casting it to a number (remember: "3" / 2is valid in JS and resolves to a number). We can get around this with a little bit of tricky typing on our map function:

我们不能在不先将键转换为数字的情况下对键执行任何算术运算(请记住:"3" / 2在 JS 中有效并解析为 a number)。我们可以通过在 map 函数上输入一些技巧来解决这个问题:

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]));
}

Here, we use the generic Sto type our object, and look up key and value types directly from that. If your object is typed using generic indexers and values, keyof Sand S[keyof S]will resolve to constant types. If you pass in an object with explicate properties, keyof Swill be restricted to the property names and S[keyof S]will be restricted to the property value types.

在这里,我们使用泛型S来键入我们的对象,并直接从中查找键和值类型。如果您的对象是使用通用索引器和值键入的,keyof S并且S[keyof S]将解析为常量类型。如果传入具有keyof S明确属性的对象,则将限制为属性名称,S[keyof S]并将限制为属性值类型。