强制泛型类型值的类型安全
Enforce type safety of a value of a generic type
我如何使用泛型来将值的类型强制为特定类型?
// An example array
const testArr = [
{
id: 3,
name: 'Spaghetto', // NOTE: Type 'string' here
shouldNotWork: 3.14, // NOTE: Type 'number' here
},
{
id: 5,
name: 'Bread',
shouldNotWork: 3.14,
},
];
这是我试图成为我的映射函数的东西,但我必须附加 as V2
以使 TS 不抱怨:/
type Mapping<T, U> = (val: T, i: number, arr: T[]) => U;
interface Option<T> {
value: T;
label: string; // <- NOTE: Type string is required
}
const typeToOption = <
T,
K1 extends keyof T,
K2 extends keyof T,
V2 extends T[K2] & string // <- NOTE: 'string' union here to match
>(
valueK: K1,
labelK: K2,
): Mapping<T, Option<T[K1]>> => (item: T): Option<T[K1]> => ({
value: item[valueK],
label: item[labelK] as V2,
});
我希望 TS 允许我这样做
const result = testArr.map(typeToOption('id', 'name'));
...但不是这个
const result = testArr.map(typeToOption('id', 'shouldNotWork'));
如何让 TS 抱怨后者?
我想你想这样输入 typeToOption()
:
const typeToOption = <
T extends Record<K2, string>,
K1 extends keyof T,
K2 extends keyof T,
>(
valueK: K1,
labelK: K2,
): Mapping<T, Option<T[K1]>> => (item: T): Option<T[K1]> => ({
value: item[valueK],
label: item[labelK],
});
TypeScript 允许您在泛型参数中做一些 "circular" 类型约束。所以在这种情况下,我们根本不需要指定 V2
(因为它没有做任何工作),我们只需要 T extends Record<K2, string>
,这意味着类型 T
必须有一个 string
在键 K2
处的值 属性。这足以为您提供所需的行为:
const result = testArr.map(typeToOption('id', 'name')); // okay
const result = testArr.map(typeToOption('id', 'shouldNotWork')); // error
// [ts] Argument of type '"id"' is not assignable to
// parameter of type '"shouldNotWork"'.
虽然错误有点莫名其妙,但至少是个错误。 (如果重要的话:类型推断失败会导致有趣的事情发生。T
退回到 Record<'shouldNotWork', string>
,相当于 {shouldNotWork: string}
,而 keyof T
只是 'shouldNotWork'
,因此 K1 extends
'shouldNotWork'`。好吧。)
希望对您有所帮助;祝你好运!
我如何使用泛型来将值的类型强制为特定类型?
// An example array
const testArr = [
{
id: 3,
name: 'Spaghetto', // NOTE: Type 'string' here
shouldNotWork: 3.14, // NOTE: Type 'number' here
},
{
id: 5,
name: 'Bread',
shouldNotWork: 3.14,
},
];
这是我试图成为我的映射函数的东西,但我必须附加 as V2
以使 TS 不抱怨:/
type Mapping<T, U> = (val: T, i: number, arr: T[]) => U;
interface Option<T> {
value: T;
label: string; // <- NOTE: Type string is required
}
const typeToOption = <
T,
K1 extends keyof T,
K2 extends keyof T,
V2 extends T[K2] & string // <- NOTE: 'string' union here to match
>(
valueK: K1,
labelK: K2,
): Mapping<T, Option<T[K1]>> => (item: T): Option<T[K1]> => ({
value: item[valueK],
label: item[labelK] as V2,
});
我希望 TS 允许我这样做
const result = testArr.map(typeToOption('id', 'name'));
...但不是这个
const result = testArr.map(typeToOption('id', 'shouldNotWork'));
如何让 TS 抱怨后者?
我想你想这样输入 typeToOption()
:
const typeToOption = <
T extends Record<K2, string>,
K1 extends keyof T,
K2 extends keyof T,
>(
valueK: K1,
labelK: K2,
): Mapping<T, Option<T[K1]>> => (item: T): Option<T[K1]> => ({
value: item[valueK],
label: item[labelK],
});
TypeScript 允许您在泛型参数中做一些 "circular" 类型约束。所以在这种情况下,我们根本不需要指定 V2
(因为它没有做任何工作),我们只需要 T extends Record<K2, string>
,这意味着类型 T
必须有一个 string
在键 K2
处的值 属性。这足以为您提供所需的行为:
const result = testArr.map(typeToOption('id', 'name')); // okay
const result = testArr.map(typeToOption('id', 'shouldNotWork')); // error
// [ts] Argument of type '"id"' is not assignable to
// parameter of type '"shouldNotWork"'.
虽然错误有点莫名其妙,但至少是个错误。 (如果重要的话:类型推断失败会导致有趣的事情发生。T
退回到 Record<'shouldNotWork', string>
,相当于 {shouldNotWork: string}
,而 keyof T
只是 'shouldNotWork'
,因此 K1 extends
'shouldNotWork'`。好吧。)
希望对您有所帮助;祝你好运!