简体   繁体   English

Typescript 通用联合类型

[英]Typescript generic union type

I'm trying to create a simple switch function which takes a first parameter that must be an union of string & an object which have keys based on the first parameter union and can return any value.我正在尝试创建一个简单的开关 function ,它采用第一个参数,该参数必须是字符串和 object 的联合,它具有基于第一个参数联合的键并且可以返回任何值。

export const mySwitch = <T extends string>(value: T, possibilities: {[key in T]: any}): any => {
    return possibilities[value];
};

Typical usage would be典型用法是

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"});

My problem occurs because the generic type T must be a string in order to be used as an object key.出现我的问题是因为泛型类型T必须是string才能用作 object 键。 I don't know how you can tell T to be an union of string .我不知道你怎么能告诉Tstring的并集。

I tried T extends string with no success.我试过T extends string没有成功。

The T extends string version seems to work well. T extends string版本似乎运行良好。 It disallows impossible , but wouldn't you want to disallow it since if the parameter can never have that value that option would be useless?:它不允许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"}); 

play

If you want to allow the extra keys you could declare the case object separately (bypassing excess property checks and allowing you to reuse the case object)如果您想允许额外的键,您可以单独声明案例 object (绕过多余的属性检查并允许您重用案例对象)


declare let option: "val1" | "val2" | "val3";
const casses = {val1: "s1", val2: "s2", val3: "s3", impossible: "impossible"}
mySwitch(option, casses); 

play

Or you could change your type a little bit so the generic type parameter is the case object, and the value will the be typed as keyof T :或者你可以稍微改变你的类型,这样泛型类型参数就是 object,值将被键入为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"}); 

play

Also a better option would be to preserve the type from the case object instead of using any :另外一个更好的选择是保留案例 object 的类型,而不是使用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

play

Edit:编辑:

To preserve both correct return type and error if there are possibilities not present in union you could use this:如果联合中不存在可能性,要保留正确的返回类型和错误,您可以使用以下命令:

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

play

Note that because typescript does control flow analysis you need to make sure option is not just types as the actual constant you assign instead of the type annotation you specify请注意,因为 typescript 进行控制流分析,您需要确保option不仅仅是类型作为您分配的实际常量,而不是您指定的类型注释

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM