如何制作“处理程序"查找“调度员"正确键入样式函数 [英] How to make "handler" lookup for "dispatcher" style function be typed correctly

查看:15
本文介绍了如何制作“处理程序"查找“调度员"正确键入样式函数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

假设我有这样的类型:

type TInfoGeneric<TType extends string, TValue> = {
  valueType: TType,
  value: TValue, // Correspond to valueType
}

为了避免重复我自己,我创建了一个类型映射,其中列出了可能的 valueType 并将 valueType 与值的类型相匹配.

To avoid repeating my self, I create a type map that list the possible valueType and matches valueType with, well, value's type.

type TInfoTypeMap = {
    num: number;
    str: string;
}

现在,要实际创建 TInfo,我使用映射类型将所有类型映射到 TInfoGeneric 中,然后只获取它的值.

Now, to actually create TInfo, I use mapped type to map all types into TInfoGeneric and then get only value side of it.

type TAllPossibleTInfoMap = {
    [P in keyof TInfoTypeMap]: TInfoGeneric<P, TInfoTypeMap[P]>;
};

type TInfo = TAllPossibleTInfoMap[keyof TAllPossibleTInfoMap]; // TInfoGeneric<"num", number> | TInfoGeneric<"str", string>

然后,为了为所有类型定义处理程序,我为处理程序创建了另一个映射类型.

Then, to define handlers for all types, I create another mapped type just for handlers.

type TInfoHandler = {
    [P in keyof TInfoTypeMap]: (value: TInfoTypeMap[P]) => any
};

const handlers: TInfoHandler = {
    num: (value) => console.log(value.toString(16)),
    str: (value) => console.log(value),
}

最后,为了实际使用处理程序,我创建了一个这样的函数:

And finally, to actually use the handler, I create a function like this:

function handleInfo(info: TInfo) {
    handlers[info.valueType](info.value); // Error
}

我收到此错误:

Argument of type 'string | number' is not assignable to parameter of type 'number & string'.
  Type 'string' is not assignable to type 'number & string'.
    Type 'string' is not assignable to type 'number'.

通常情况下,handlers[info.valueType] 可以是一个 ((value: number) => any) | 是可以理解的.((value: string) => any).但是,在这种情况下:

Normally, it's understandable that handlers[info.valueType] may be a ((value: number) => any) | ((value: string) => any). However, in this case:

  • 如果info.valueType'num',那么我们可以确定handlers[info.valueType](值:数字)=>any)info.valuenumber.因此,可以使用 info.value 调用 handlers[info.valueType].
  • 如果info.valueType'str',那么我们可以确定handlers[info.valueType](值:字符串) =>any)info.valuestring.因此,可以使用 info.value 调用 handlers[info.valueType].
  • If info.valueType is 'num', then we can be sure that handlers[info.valueType] is (value: number) => any) and info.value is number. Thus, handlers[info.valueType] can be called with info.value.
  • If info.valueType is 'str', then we can be sure that handlers[info.valueType] is (value: string) => any) and info.value is string. Thus, handlers[info.valueType] can be called with info.value.

我不确定这是否是 Typescript 的限制,但是否可以以这种风格编写代码以进行类型检查?

I'm not sure if this is Typescript limitation or not, but is it possible to write the code in this style so that it's type-checked?

推荐答案

是的,这里没有适合您的方便且类型安全的解决方案.我已经在 microsoft/TypeScript#30581 上打开了一个问题,但我没有不希望它得到解决.

Yeah, there's no convenient and type-safe solution for you here. I've opened an issue at microsoft/TypeScript#30581 about this but I don't expect it to be addressed.

我看到了两种主要的前进方向.一种是只使用 类型断言,因为你理所当然地知道比编译器在这里做的更多.可能是这样的:

I see two main ways forward. One is to just use a type assertion, since you legitimately know more than the compiler does here. It could be like this:

function handleInfo(info: TInfo) {
    // assert your way out.  Not type safe but convenient!
    (handlers[info.valueType] as (x: number | string)=>any)(info.value); 
}

现在没有错误.它不是类型安全的.但它很方便,并且不会改变发出的 JavaScript.

Now there's no error. It's not type safe. But it's convenient and doesn't change the emitted JavaScript.

或者您可以尝试通过案例引导编译器并向其证明一切正常.这很复杂、很脆弱,并且会产生运行时影响:

Or you could try to walk the compiler through the cases and prove to it that all is fine. This is complex, brittle, and has runtime effects:

const typeGuards: {
  [P in keyof TInfoTypeMap]: (x: TInfoTypeMap[keyof TInfoTypeMap])=>x is TInfoTypeMap[P];
} = {
    num: (x:any): x is number => typeof x === "number",
    str: (x:any): x is string => typeof x === "string"
}

function narrowTInfo<K extends keyof TAllPossibleTInfoMap>(
  x: TInfo, v: K): x is TAllPossibleTInfoMap[K] {
    return typeGuards[v](x.value);
} 

function handleInfo(info: TInfo) {
    if (narrowTInfo(info, "num")) {
        handlers[info.valueType](info.value); // okay
    } else {
        handlers[info.valueType](info.value); // okay
    }
}

这可行,但令人讨厌.所以我推荐一个断言.

That works but is obnoxious. So I'd recommend an assertion.

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

Hope that helps; good luck!

这篇关于如何制作“处理程序"查找“调度员"正确键入样式函数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆