打字稿通用联合类型
Typescript generic union type
我正在尝试创建一个简单的 switch 函数,它采用第一个参数,该参数必须是字符串的并集和一个对象,该对象具有基于第一个参数并集的键并且可以 return 任何值。
export const mySwitch = <T extends string>(value: T, possibilities: {[key in T]: any}): any => {
return possibilities[value];
};
典型用法是
let option: "val1" | "val2" | "val3" = "val1";
// should returns s1
// Impossible should be type-checked as an error since it's not part of the option union type
mySwitch(option, {val1: "s1", val2: "s2", val3: "s3", impossible: "impossible"});
我的问题出现是因为泛型类型 T
必须是 string
才能用作对象键。我不知道你怎么知道 T
是 string
.
的并集
我尝试了 T extends string
但没有成功。
T extends string
版本似乎运行良好。它不允许 impossible
,但您不想禁止它吗,因为如果参数永远不能具有该值,那么该选项将毫无用处?:
export const mySwitch = <T extends string>(value: T, possibilities: {[key in T]: any}): any => {
return possibilities[value];
};
declare let option: "val1" | "val2" | "val3";
mySwitch(option, {val1: "s1", val2: "s2", val3: "s3", impossible: "impossible"});
如果你想允许额外的键,你可以单独声明 case 对象(绕过多余的 属性 检查并允许你重用 case 对象)
declare let option: "val1" | "val2" | "val3";
const casses = {val1: "s1", val2: "s2", val3: "s3", impossible: "impossible"}
mySwitch(option, casses);
或者你可以稍微改变你的类型,这样泛型类型参数就是 case 对象,值将被键入为 keyof T
:
export const mySwitch = <T>(value: keyof T, possibilities: T): any => {
return possibilities[value];
};
declare let option: "val1" | "val2" | "val3";
mySwitch(option, {val1: "s1", val2: "s2", val3: "s3", impossible: "impossible"});
另外一个更好的选择是保留 case 对象的类型而不是使用 any
:
export const mySwitch = <T, K extends keyof T>(value: K, possibilities: T): T[K] => {
return possibilities[value];
};
declare let option: "val1" | "val2" | "val3";
mySwitch(option, {val1: 1, val2: "s2", val3: "s3", impossible: false}); // returns string | number
编辑:
要同时保留正确的 return 类型和错误(如果联合中不存在的可能性),您可以使用此方法:
const mySwitch = <T extends Record<K, any>, K extends string>(value: K, possibilities: T & Record<Exclude<keyof T, K>, never>): any => {
return possibilities[value];
};
let option: "val1" | "val2" | "val3" = (["val1", "val2", "val3"] as const)[Math.round(Math.random() * 2)]
mySwitch(option, {val1: "s1", val2: "s2", val3: "s3" });
mySwitch(option, {val1: "s1", val2: "s2", val3: "s3", impossible: "" }); //err on impossible
请注意,由于 typescript 会控制流分析,因此您需要确保 option
不只是类型作为您分配的实际常量,而不是您指定的类型注释
我正在尝试创建一个简单的 switch 函数,它采用第一个参数,该参数必须是字符串的并集和一个对象,该对象具有基于第一个参数并集的键并且可以 return 任何值。
export const mySwitch = <T extends string>(value: T, possibilities: {[key in T]: any}): any => {
return possibilities[value];
};
典型用法是
let option: "val1" | "val2" | "val3" = "val1";
// should returns s1
// Impossible should be type-checked as an error since it's not part of the option union type
mySwitch(option, {val1: "s1", val2: "s2", val3: "s3", impossible: "impossible"});
我的问题出现是因为泛型类型 T
必须是 string
才能用作对象键。我不知道你怎么知道 T
是 string
.
我尝试了 T extends string
但没有成功。
T extends string
版本似乎运行良好。它不允许 impossible
,但您不想禁止它吗,因为如果参数永远不能具有该值,那么该选项将毫无用处?:
export const mySwitch = <T extends string>(value: T, possibilities: {[key in T]: any}): any => {
return possibilities[value];
};
declare let option: "val1" | "val2" | "val3";
mySwitch(option, {val1: "s1", val2: "s2", val3: "s3", impossible: "impossible"});
如果你想允许额外的键,你可以单独声明 case 对象(绕过多余的 属性 检查并允许你重用 case 对象)
declare let option: "val1" | "val2" | "val3";
const casses = {val1: "s1", val2: "s2", val3: "s3", impossible: "impossible"}
mySwitch(option, casses);
或者你可以稍微改变你的类型,这样泛型类型参数就是 case 对象,值将被键入为 keyof T
:
export const mySwitch = <T>(value: keyof T, possibilities: T): any => {
return possibilities[value];
};
declare let option: "val1" | "val2" | "val3";
mySwitch(option, {val1: "s1", val2: "s2", val3: "s3", impossible: "impossible"});
另外一个更好的选择是保留 case 对象的类型而不是使用 any
:
export const mySwitch = <T, K extends keyof T>(value: K, possibilities: T): T[K] => {
return possibilities[value];
};
declare let option: "val1" | "val2" | "val3";
mySwitch(option, {val1: 1, val2: "s2", val3: "s3", impossible: false}); // returns string | number
编辑:
要同时保留正确的 return 类型和错误(如果联合中不存在的可能性),您可以使用此方法:
const mySwitch = <T extends Record<K, any>, K extends string>(value: K, possibilities: T & Record<Exclude<keyof T, K>, never>): any => {
return possibilities[value];
};
let option: "val1" | "val2" | "val3" = (["val1", "val2", "val3"] as const)[Math.round(Math.random() * 2)]
mySwitch(option, {val1: "s1", val2: "s2", val3: "s3" });
mySwitch(option, {val1: "s1", val2: "s2", val3: "s3", impossible: "" }); //err on impossible
请注意,由于 typescript 会控制流分析,因此您需要确保 option
不只是类型作为您分配的实际常量,而不是您指定的类型注释