TypeScript - 通用约束可以提供 "allowed" 类型吗?
TypeScript - can a generic constraint provide "allowed" types?
给定以下代码...
type Indexable<TKey, TValue> = { [index: TKey]: TValue }
这会产生以下错误:
An index signature parameter type must be 'string' or 'number'.
有没有办法将 TKey
限制为 'string' 或 'number'?
您可以将 TKey 限制为从字符串或数字派生(使用扩展),但这不会满足编译器的要求。 index
必须是数字或字符串,不能是通用类型或任何其他类型。这记录在 language spec
如@TitianCernicova-Dragomir indicates, you can't use TKey
as the type in an index signature, even if it is equivalent to string
or number
。
如果您知道 TKey
正好是 string
或 number
,您可以直接使用它,而无需在您的类型中指定 TKey
:
type StringIndexable<TValue> = { [index: string]: TValue }
type NumberIndexable<TValue> = { [index: number]: TValue }
旁白:TypeScript 对待 number
通常被视为 string
的一种 子类型 for key类型。那是因为在 JavaScript 中,索引在您使用它们时无论如何都会转换为 string
,从而导致这种行为:
const a = { 0: "hello" };
console.log(a[0]); // outputs "hello"
console.log(a['0']) // *still* outputs "hello"
编辑:注意 TS2.9 添加了 support for number
and even symbol
keys in mapped types。我们将使用 keyof any
表示 "whatever your version of TypeScript thinks are valid key types"。返回其余答案:
如果你想让TKey
比keyof any
更具体,意思是只有某些键是允许的,你可以使用 mapped types:
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};
如果您不想将键限制为特定文字或文字并集,您仍然可以使用 string
作为 TKey
:
const anyNums: Indexable<string, number> = { uno: 1, zwei: 2, trois: 3 };
事实上,Indexable<TKey, TValue>
的这个定义非常有用,它已经存在于 TypeScript standard library as Record<K,T>
:
type NumNames = 'zero' | 'one' | 'two';
const nums: Record<NumNames, number> = { zero: 0, one: 1, two: 2 };
因此,我建议您将 Record<K,T>
用于这些目的,因为它是标准的,并且阅读您的代码的其他 TypeScript 开发人员更有可能熟悉它。
希望对您有所帮助;祝你好运!
给定以下代码...
type Indexable<TKey, TValue> = { [index: TKey]: TValue }
这会产生以下错误:
An index signature parameter type must be 'string' or 'number'.
有没有办法将 TKey
限制为 'string' 或 'number'?
您可以将 TKey 限制为从字符串或数字派生(使用扩展),但这不会满足编译器的要求。 index
必须是数字或字符串,不能是通用类型或任何其他类型。这记录在 language spec
如@TitianCernicova-Dragomir indicates, you can't use TKey
as the type in an index signature, even if it is equivalent to string
or number
。
如果您知道 TKey
正好是 string
或 number
,您可以直接使用它,而无需在您的类型中指定 TKey
:
type StringIndexable<TValue> = { [index: string]: TValue }
type NumberIndexable<TValue> = { [index: number]: TValue }
旁白:TypeScript 对待 number
通常被视为 string
的一种 子类型 for key类型。那是因为在 JavaScript 中,索引在您使用它们时无论如何都会转换为 string
,从而导致这种行为:
const a = { 0: "hello" };
console.log(a[0]); // outputs "hello"
console.log(a['0']) // *still* outputs "hello"
编辑:注意 TS2.9 添加了 support for number
and even symbol
keys in mapped types。我们将使用 keyof any
表示 "whatever your version of TypeScript thinks are valid key types"。返回其余答案:
如果你想让TKey
比keyof any
更具体,意思是只有某些键是允许的,你可以使用 mapped types:
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};
如果您不想将键限制为特定文字或文字并集,您仍然可以使用 string
作为 TKey
:
const anyNums: Indexable<string, number> = { uno: 1, zwei: 2, trois: 3 };
事实上,Indexable<TKey, TValue>
的这个定义非常有用,它已经存在于 TypeScript standard library as Record<K,T>
:
type NumNames = 'zero' | 'one' | 'two';
const nums: Record<NumNames, number> = { zero: 0, one: 1, two: 2 };
因此,我建议您将 Record<K,T>
用于这些目的,因为它是标准的,并且阅读您的代码的其他 TypeScript 开发人员更有可能熟悉它。
希望对您有所帮助;祝你好运!