12

鉴于以下代码...

type Indexable<TKey, TValue> = { [index: TKey]: TValue }

这会产生以下错误:

索引签名参数类型必须是“字符串”或“数字”。

有没有办法限制TKey为“字符串”或“数字”?

4

2 回答 2

9

正如@TitianCernicova-Dragomir所指出的,您不能将TKey其用作索引签名中的类型,即使它等同于stringornumber

如果你知道它TKeystringor number,你可以直接使用它而不是TKey在你的类型中指定:

type StringIndexable<TValue> = { [index: string]: TValue }
type NumberIndexable<TValue> = { [index: number]: TValue }

旁白:TypeScript 对待number通常被视为键类型的一种子类型string。那是因为在 JavaScript 中,string当你使用它们时,索引会被转换为反正,导致这种行为:

const a = { 0: "hello" };
console.log(a[0]); // outputs "hello"
console.log(a['0']) // *still* outputs "hello"

编辑:请注意,TS2.9 添加了对映射类型甚至键的支持numbersymbol。我们将keyof any用来表示“您的 TypeScript 版本认为是有效的键类型”。回到答案的其余部分:


如果您想允许TKey比更具体keyof any,这意味着只允许某些键,您可以使用映射类型

type Indexable<TKey extends keyof any, TValue> = { [K in TKey]: TValue }

您可以通过传入字符串文字或字符串文字的联合来使用它TKey

type NumNames = 'zero' | 'one' | 'two';
const nums: Indexable<NumNames, number> = { zero: 0, one: 1, two: 2 };

type NumNumerals = '0' | '1' | '2';
const numerals: Indexable<NumNumerals, number> = {0: 0, 1: 1, 2: 2};

如果您不想将键限制为特定文字或文字联合,您仍然可以使用stringas TKey

const anyNums: Indexable<string, number> = { uno: 1, zwei: 2, trois: 3 };

事实上,这个 for 的定义Indexable<TKey, TValue>非常有用,它已经存在于TypeScript 标准库中Record<K,T>

type NumNames = 'zero' | 'one' | 'two';
const nums: Record<NumNames, number> = { zero: 0, one: 1, two: 2 };

因此,我建议您将Record<K,T>其用于这些目的,因为它是标准的,并且阅读您的代码的其他 TypeScript 开发人员更有可能熟悉它。


希望有帮助;祝你好运!

于 2017-10-23T12:52:48.900 回答
6

您可以将 TKey 限制为从字符串或数字派生(使用扩展),但这不会满足编译器的要求。index必须是数字或字符串,而不是泛型类型或任何其他类型。这记录在语言规范中

于 2017-10-23T09:07:06.487 回答