Typescript 中的异构函数映射 [英] Map of heterogeneous functions in Typescript

查看:67
本文介绍了Typescript 中的异构函数映射的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想在 Typescript 中创建不同函数的注册表.

I want to create a registry of different functions in Typescript.

例如,我有几个具有不同参数类型的函数:

For example I have several functions with different arguments types:

const func1 = (p: { x: number }) => {
  console.log("number: " + p.x);
};
const func2 = (p: { s: string }) => {
  console.log("string: " + p.s);
};

我和他们一起创建了一个注册表并给他们起了名字:

I create a registry with them and giving them names:

const map = {
  number: func1,
  string: func2
};

然后我想推断以下类型:

Then I want to infer following type:

{ funcName: "number", x: number } | { funcName: "string", s: string }

此类型保存有关注册表中的函数名称及其参数类型的信息.我可以通过以下代码自动执行此操作:

This type holds the information about function name in registry and its arguments type. I can do this automatically by following code:

type Keys = keyof typeof map;

// assume all functions have single input argument
type ArgByName<Key extends Keys> = Parameters<typeof map[Key]>[0];

type ArgExtendedByName<Key extends Keys> = ArgByName<Key> & { funcName: Key };

type ArgType = { [Key in Keys]: ArgExtendedByName<Key> }[Keys];

最后我写了一个函数来调用注册表中的函数:

Finally I write a function to call functions from registry:

function Apply(args: ArgType) {
  const { funcName, ...funcArgs } = args
  map[funcName](funcArgs);
}

但我收到以下错误:

Argument of type "{ s: string; } | { x: number; }" is not assignable to parameter of type "{ x: number; } & { s: string; }".
  Type "{ s: string; }" is not assignable to type "{ x: number; } & { s: string; }".
    Property 'x' is missing in type '{ s: string; }' but required in type '{ x: number; }'.ts(2345)
index.ts(1, 21): 'x' is declared here.

我真的不明白,为什么函数的输入参数变成了这个{ x: number;&{ s:字符串;},但不是这个 { s: string;} |{ x:数量;}".是Typescript bug还是我的错误?这个问题怎么解决?

I really cannot understand, why the input arguments of functions became this { x: number; } & { s: string; }, but not this { s: string; } | { x: number; }". Is it Typescript bug or my error? How does this problem can be solved?

还有一个包含上述所有代码的游乐场:https://codesandbox.io/s/sad-mccarthy-nvb47?fontsize=14&hidenavigation=1&theme=dark

Also here is a playground with all code above: https://codesandbox.io/s/sad-mccarthy-nvb47?fontsize=14&hidenavigation=1&theme=dark

推荐答案

这看起来像是我一直在调用的另一个案例 相关记录类型;您有多个联合类型的值,但它们彼此相关,但编译器将它们视为不相关.

This looks like another case of what I've been calling correlated record types; you have multiple values of union types but they are correlated to each other, but the compiler treats them as uncorrelated.

一般来说,如果你有一个 [x, y] 类型的值 [A, B] |[C, D],编译器最终会将 xy 视为 A | 类型的自变量.CB |D 分别.因此 [x, y] 突然被当作 [A, B] | 对待.[A, D] |[C, B] |[C, D] 与那些不可能的 [A, D][C, B] 互相关术语在那里.编译器忘记了相关性,然后抱怨忘记了.

In general, if you have a value [x, y] of type [A, B] | [C, D], the compiler will end up treating x and y as independent variables of type A | C and B | D respectively. Therefore [x, y] is suddenly being treated like [A, B] | [A, D] | [C, B] | [C, D] with those impossible [A, D] and [C, B] cross-correlated terms in there. The compiler forgets the correlation, and then complains about having forgotten.

TypeScript 中对这类事情的支持并不多.有时您可以将此类代码重构为一个通用函数,该函数的行为符合您的要求,但通常您要么需要复制代码,如下所示:

There's not much in the way of support for this sort of thing in TypeScript. Sometimes you can refactor such code into a generic function which behaves as you want, but in general you either need to duplicate code, like this:

function ApplyRedundant(args: ArgType) {
    if (args.funcName === "string") {
        const { funcName, ...funcArgs } = args;
        map[funcName](funcArgs);
    } else {
        const { funcName, ...funcArgs } = args;
        map[funcName](funcArgs);
    }
}

或者使用类似类型断言:

function ApplyAssert(args: ArgType) {
    const { funcName, ...funcArgs } = args;
    map[funcName](funcArgs as ArgByName<"string"> & ArgByName<"number">);
}

我通常建议进行类型断言.请注意,我已经断言从并集到交集;这是因为 TypeScript 3.3 中对 允许调用具有参数交集的函数的联合.在此更改之前,您的调用只是一个错误,提示您根本无法调用它".之后它说我不知道(或忘记)这个函数是想要一个 ArgByName<"string"> 还是它想要一个 ArgByName<"number"> 所以你最好同时向我传递两者的东西,这样我就知道无论是哪​​一个它都会起作用."

I usually recommend doing a type assertion. Note that I've asserted from the union to the intersection; that's because of a change introduced in TypeScript 3.3 to allow calling unions of functions with intersections of arguments. Before this change your call would just have been an error saying "you can't call this at all". Afterward it's saying "I don't know (or forgot) whether this function wants an ArgByName<"string"> or whether it wants an ArgByName<"number"> so you'd better pass me something that's both of them at the same time, so I know it will work no matter which one it is."

哦,好吧.好的,希望有帮助;祝你好运!

Oh well. Okay, hope that helps; good luck!

游乐场连结代码

这篇关于Typescript 中的异构函数映射的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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