应用函数名称和参数的函数类型 [英] Types for function that applys name of function and arguments

查看:29
本文介绍了应用函数名称和参数的函数类型的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试以正确的方式键入函数,该函数为该函数应用函数名称和参数.之后应用它并返回结果.代码如下:

I'm trying to type in proper way function that applys name of function and argumens for this function. After that apply it and return the result. Here the code:

const sum = (a: number, b: number) => a + b
const concat = (a: string, b: string, c: string) => a + b + c

const funs = {
    sum,
    concat
}

type Keys = 'sum' | 'concat'

type Args<T> = T extends (...args: infer R) => any ? R : never

type Sum = Args<typeof sum>
type Concat = Args<typeof concat>

function apply<K extends Keys>(funKey: K, ...args: Args<typeof funs[K]>) {
    // here I get the error 'An argument for 'a' was not provided.'
    return funs[funKey](...args)
}

const test1 = apply('sum', 1, 2)
const test2 = apply('concat', 'str1', 'str2', 'str3' )

Inside funciton apply 我收到错误未提供‘a’的参数.".我怎样才能摆脱这个错误?

Inside funciton apply I get the error 'An argument for 'a' was not provided.'. How I can get rid of this error?

游乐场链接

推荐答案

编译器将无法理解这是类型安全的,因为它通常不能很好地推理依赖于 as-yet-unspecified 的类型的可分配性泛型类型参数.现有的 GitHub 问题 microsoft/TypeScript#24085 描述了这种情况.

The compiler will not be able to understand that this is type safe because it generally does not reason very well about assignability for types that depend on as-yet-unspecified generic type parameters. There is an existing GitHub issue, microsoft/TypeScript#24085, that describes this situation.

事实上,有可能(但不太可能)在您的函数中,K 可能被推断为 Keys 本身而不是 sum";concat".如果你这样做:

In fact, it is possible (but not very likely) that in your function, K might be inferred as Keys itself instead of either "sum" or "concat". If you do this:

const oops = apply(Math.random() < 0.5 ? "sum" : "concat", "a", "b", "c"); // oopsie
console.log(oops); // 50% chance of "abc", 50% chance of "ab"

然后你会看到编译器技术上是正确的,你所做的不是类型安全的.您想告诉编译器 K恰好是 Keys 的成员之一,但您不能.请参阅 microsoft/TypeScript#27808 了解允许这样做的功能建议.

then you see that the compiler is technically correct that what you're doing isn't type safe. You'd like to tell the compiler that K will be exactly one of the members of Keys, and you can't. See microsoft/TypeScript#27808 for a feature suggestion that would allow this.

无论如何,编译器无法将 funKey 参数和 args rest 参数视为具有 correlated 类型.即使可以,也不能很好地保持相关性,请参阅 microsoft/TypeScript#30581 了解更多.

Anyway, the compiler can't view the funKey parameter and the args rest parameter as having correlated types. And even if it could, it's not great at maintaining the correlation, see microsoft/TypeScript#30581 for more about that.

它也无法理解计算返回类型,因此您必须对其进行注释.您可以使用 ReturnType 实用程序为此输入.请注意,还有一个 Parameters实用程序类型,您可以使用它来代替自己编写Args.

It also can't understand compute the return type, so you'll have to annotate it. You can use the ReturnType<F> utility type for this. Note that there's also a Parameters<F> utility type which you can use instead of writing Args<F> yourself.

因此,归根结底,您只需要告诉编译器您所做的是类型安全的(您不会在某些联合类型的 上调用 apply()>funKey,对吧?),因为它无法验证.为此,您需要类似于 类型断言.在这里最容易使用的是旧的 any:

So when it comes down to it, you will just have to tell the compiler that what you are doing is type safe (you won't call apply() on some union-typed funKey, right?), because it can't verify it. And to do that you need something like a type assertion. The easiest one to use here is good old any:

type Funs = typeof funs;

function apply<K extends Keys>(funKey: K, ...args: Parameters<Funs[K]>): ReturnType<Funs[K]> {
    return (funs[funKey] as any)(...args);
}

这将允许你做一些疯狂的事情,比如return (funs[funKey] as any)(true),所以你应该小心.将 funs[funKey] 表示为一个函数,它以某种方式接受每个函数期望的参数,并返回 both类型.像这样:

This will allow you to do crazy things like return (funs[funKey] as any)(true), so you should be careful. Slightly more type safe but considerably more complicated is to represent funs[funKey] as a function which somehow accepts either the arguments expected by each function, and which returns both of the return types. Like this:

type WidenFunc<T> = ((x: T) => void) extends ((x: (...args: infer A) => infer R) => any) ?
    (...args: A) => R : never;

function apply<K extends Keys>(funKey: K, ...args: Parameters<Funs[K]>): ReturnType<Funs[K]> {
    return (funs[funKey] as WidenFunc<Funs[Keys]>)(...args);
}

这里 WidenFunc(...args: [number, number] | [string, string, string]) =>数&字符串.这是一种无意义的函数类型,但至少如果你向它传递一个像 (true) 而不是 (...args) 这样的参数,它会抱怨.

Here WidenFunc<Funs[Keys]> is (...args: [number, number] | [string, string, string]) => number & string. That's kind of a nonsense function type, but at least it will complain if you pass it an argument like (true) instead of (...args).

无论如何,其中任何一个都应该有效:

Anyway, either of those should work:

const test1 = apply('sum', 1, 2) // number
const test2 = apply('concat', 'str1', 'str2', 'str3') // string


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


Okay, hope that helps; good luck!

Playground 代码链接

这篇关于应用函数名称和参数的函数类型的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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