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

时间:2017-10-23 09:01:58

标签: typescript generics generic-constraints

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

TKey

这会产生以下错误:

  

索引签名参数类型必须是'string'或'number'。

有没有办法将{{1}}限制为'字符串'或'数字'?

2 个答案:

答案 0 :(得分:5)

您可以将TKey约束为从字符串或数字派生(使用extends)但不满足编译器。 index必须是数字或字符串,不是通用类型或任何其他类型。这在language spec

中有记录

答案 1 :(得分:4)

@TitianCernicova-Dragomir所示,您不能使用TKey作为索引签名中的类型,即使它是equivalent to string or number

如果您知道TKey完全是stringnumber,则可以直接使用它,而不是在您的类型中指定TKey

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

除了:事实证明,在TypeScript的许多地方,属性的索引类型必须是string,而不是number。在这些地方,number通常被视为string的一种子类型。这是因为在JavaScript中,当您使用索引时,索引会转换为string,从而导致这种行为:

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

由于您无法在后面使用number,我会忽略它;如果你需要使用数字键TypeScript可能会让你,或者你可以手动转换为string。回到答案的其余部分:

如果您希望TKey 更具体而不是string,意味着只允许某些键,您可以使用{{ 3}}:

type Indexable<TKey extends string, 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};

如果您不想将密钥限制为特定文字或文字联盟,您仍然可以将string用作TKey

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

事实上,Indexable<TKey, TValue>的这个定义非常有用,它已经存在于mapped types中:

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

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

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