TypeScript - 泛型约束可以提供"允许"类型吗?

ser*_*0ne 9 generics generic-constraints typescript

鉴于以下代码......

type Indexable<TKey, TValue> = { [index: TKey]: TValue }
Run Code Online (Sandbox Code Playgroud)

这会产生以下错误:

索引签名参数类型必须是"字符串"或"数字".

有没有办法约束TKey为'字符串'或'数字'?

Tit*_*mir 5

您可以将TKey约束为从字符串或数字派生(使用extends)但不满足编译器.index必须是数字或字符串,不是通用类型或任何其他类型.这在语言规范中有记录


jca*_*alz 5

就像@ TitianCernicova-Dragomir指出的TKey那样,即使它等于stringnumber也不能用作索引签名中的类型。

如果您知道TKey确切地是stringnumber,则可以直接使用它,而无需TKey在您的类型中指定:

type StringIndexable<TValue> = { [index: string]: TValue }
type NumberIndexable<TValue> = { [index: number]: TValue }
Run Code Online (Sandbox Code Playgroud)

旁白:事实证明,在TypeScript的许多地方,属性的索引类型必须是string,而不是number。在这些地方,number通常被视为一种亚型string。这是因为在JavaScript中,无论string何时使用索引,索引都将转换为索引,从而导致这种行为:

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

由于您不能number在随后的内容中使用,因此我将忽略它;如果您需要使用数字键,TypeScript可能会让您使用,也可以string手动转换为。返回其余的答案:


如果你想允许TKey更具体的string,这意味着只有某些键被允许,您可以使用映射的类型

type Indexable<TKey extends string, TValue> = { [K in TKey]: TValue }
Run Code Online (Sandbox Code Playgroud)

您可以通过传入以下字符串文字或字符串文字并集来使用它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};
Run Code Online (Sandbox Code Playgroud)

如果你不希望限制的关键,尤其是文字或文字的工会,你仍然可以使用stringTKey

const anyNums: Indexable<string, number> = { uno: 1, zwei: 2, trois: 3 };
Run Code Online (Sandbox Code Playgroud)

实际上,这个定义Indexable<TKey, TValue>非常有用,它已经存在于TypeScript标准库中,如下所示Record<K,T>

type NumNames = 'zero' | 'one' | 'two';
const nums: Record<NumNames, number> = { zero: 0, one: 1, two: 2 };
Run Code Online (Sandbox Code Playgroud)

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


希望能有所帮助;祝好运!