假设我们想要定义一个接受另一个函数及其参数的函数,并输出与该参数一起调用的函数。这是一个很好的例子,我们希望打字稿能做适当的案例分析。见下文。
let fns = {
A: (x: string) => 'a' + x,
B: (y: number) => 100 + y
}
type fn = typeof fns[keyof typeof fns]
const combine = <T extends fn>(fn: T, param: Parameters<T>[0]) => {
return fn(param)
}问题是,打字本给了我下面这个单词param的错误(在单词返回的行上):Argument of type 'string | number' is not assignable to parameter of type 'never'. Type 'string' is not assignable to type 'never'.ts(2345)
由于某些原因,它将这两种类型的输入合并到一种类型中,而不是理解T只会是一种或另一种。帮助!
发布于 2022-09-18 13:50:45
(使用A和B分别指签名)
TypeScript在这里实际上是正确的,因为T extends fn是,而不是,意思是T只能是"A“或"B”。T extends fn是一个约束,这意味着T必须分配给fn。当然,"A“和"B”可以分配给fn,但是类型A | B也可以分配给fn。
这意味着有人可以这样调用你的功能:
combine<fn>(fns.A, 0);如果TypeScript允许这样做,那么这个调用将是有效的!这将导致意想不到的不良行为。但别担心,我有两个可能的解决办法,每一个都有各自的优缺点。
一种是使用外部签名并编写如下内部签名:
function combine<T extends fn>(fn: T, param: Parameters<T>[0]): ReturnType<T>;
function combine<T extends (x: unknown) => R, R>(fn: T, param: Parameters<T>[0]): R {
return fn(param);
}这是有点丑陋和不安全,但它的工作,同时保留原来的签名。但是,正如我前面提到的,人们可以用fn类型来称呼它,而不是"A“或"B”。
与第一种类似,它使用单独的重载代替。它比第一个稍微好一点,因为它阻止用户用函数联合调用它,但它要输入的内容要多得多。
function combine(fn: typeof fns.A, param: string): string;
function combine(fn: typeof fns.B, param: number): number;
function combine<T extends (x: unknown) => R, R>(fn: T, param: Parameters<T>[0]): R {
return fn(param)
}也许有更好的解决方案,但我还没喝早咖啡。希望你至少理解为什么这是一个错误,但我仍然会编辑更好的解决方案,当我想到他们的答案。
https://stackoverflow.com/questions/73760581
复制相似问题