应用函数名称和参数的函数类型

伊万

我试图以适当的方式键入为该功能应用功能名称和参数的功能。之后应用它并返回结果。这里的代码:

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' )

apply函数内部,出现错误“未提供'a'的参数”。我如何摆脱这个错误?

链接playgound:http://www.typescriptlang.org/play/?ssl=22&ssc=1&pln=23&pc=1#code/MYewdgzgLgBBCuBbGBeGAKAhgLhmJARgKYBOANDAbvosSQJSoB8MmMA1JQFCiSy-BMsNFlzQSASzABzClThRJMisDGKp0xihZtOBDjGBce4aDABm8SKhgBvLjEdwkZB04FCuAX2NQAngAORDAA0kR+EDYA5AiIUTAAPjBRHlBRvoHBAIIk0hAAPAAqLGiFMEQAHlBEYAAmkegAdM2YuRC4UuakMABKWjpgfjAA-L0w1EQAbqQZQTAAykg2OXn5-kEg5s6ITFzrwQDC4ILCMCsF+5uGx0K7XJZgwFAS4KwBAQA2fvkh5VU19VC4QgTHQDzCflwIQozUarTyuHOa0yVweEAA2iEALpMRj2JwwEhEKDwEhgCxWDHg8JYpotNr0bzGXhmarQACMNkw7y+6BiSCiFHZFAATIyWbA2VARVyeX4+alBclxOylTFFCK1eIAMzxRlAA

卡兹

编译器将无法理解这是类型安全的,因为对于通常依赖于尚未指定的通用类型参数的类型,它通常不能很好地说明其可分配性。存在一个存在GitHub问题microsoft / TypeScript#13995,它描述了这种情况。

实际上,可能(但不太可能)在您的函数K中将Keys推断为本身而不是"sum"or "concat"如果您这样做:

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

无论如何,编译器无法将funKey参数和argsrest参数视为具有相关类型。即使可以,但保持相关性也不是很好,请参阅microsoft / TypeScript#30581了解更多信息。

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


因此,归根结底,您只需要告诉编译器您正在执行的操作是类型安全的(您不会调用apply()某些union-typed funKey,对吗?),因为它无法对其进行验证。为此,您需要类似类型断言的东西在这里最容易使用的是好旧的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]为一个函数,该函数以某种方式接受每个函数期望的参数,并返回两种返回类型。像这样:

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<Funs[Keys]>(...args: [number, number] | [string, string, string]) => number & string这是一种胡说八道的函数类型,但是如果您将类似的参数传递给(true)而不是,它将至少会抱怨(...args)


无论如何,这两个都应该起作用:

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

好吧,希望能有所帮助;祝好运!

操场上的代码链接

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章