TypeScript:如何使用 return 类型作为参数回调中的 T?

TypeScript: How to use return type as T from argument callback?

目标:

我希望它使用第二次回调中的 return 值。

问题:

它似乎指向一个未知类型,我该如何解决?

function Test1<T>( msg, fn:(arg:T)=>void, fnTake:(any)=>T )
function Test1( msg, fn, fnTake ) {}

Test1( 'test', (arg) =>{ arg.ZZ }, m => { ZZ:123 } );
//             the 'arg' is unknown

code

您可以在测试中为 arg 添加任何 as 类型。

Test1( 'test', (arg: any) =>{ arg.ZZ }, m => { ZZ:123 } );

调用函数时必须将arg的类型传递给Test1:

type Arg = {
  ZZ: number
}

Test1<Arg>( 'test', (arg) =>{ arg.ZZ }, m => { ZZ:123 } );

初始化函数时的Test1<T>意味着调用它时传递给Test1的类型将是arg

的类型

这是 TypeScript 的设计限制。有许多 GitHub 个问题与此相关; microsoft/TypeScript#25826 可能是一个很好的重点。

当您调用 Test1 时,编译器需要推断泛型类型参数 T,以及回调参数的上下文类型 arg。它在几个 "passes" 中执行此操作,但不幸的是,它在您的情况下以错误的顺序执行这些操作。

我不知道具体发生了什么,但草图看起来像这样。当你打电话时,

Test1('test', (arg) => { arg.ZZ }, m => ({ ZZ: 123 }));

编译器既希望从 T 推断出 arg 的类型,又希望从 arg 推断出 T 的类型,但它不能这样做,所以它将推理推迟到以后。它继续进行第二个回调。这个也有一个未注释的论点,所以它在这里也推迟了推理。在下一次传递时,它必须尝试推断类型参数 T,但在检查第一个回调时仍然没有信息可供它使用。它现在必须选择一些东西,所以它失败并选择 unknown。你得到了你的错误。


可能应该改进 TypeScript 的推理算法来处理此类事情,但这可能是一个巨大的破坏性变化。根据a comment in that GitHub issue

After a fairly long discussion of options here, we don't have any ideas for how to fix this without breaking other scenarios. Full unification is of course a "solution" but that's basically a complete ground-up rewrite, so not really in the cards for the time being.

提到的"full unification"是microsoft/TypeScript#30134的主题,但似乎没有立即计划在这里做任何事情。


因此,除非解决此问题,否则还有解决方法。最简单的解决方法是将参数注释为第二个回调:

Test1('test', arg => { arg.ZZ }, (m: any) => ({ ZZ: 123 })) // T is {ZZ: number}

这会延迟 arg => ... 的推理,然后 不会 延迟 (m: any) => ...。它知道那里的类型,并且看到 return 类型是 {ZZ: number}现在 它可以从中正确推断出 T,并且在第二遍中,已知 arg 的类型为 {ZZ: number},一切都很好。

同样,您可以指定类型来帮助推理,方法是在调用 Test1:

时注释 arg 或指定 T
Test1('test', (arg: { ZZ: number }) => { arg.ZZ }, m => ({ ZZ: 123 })) // T is {ZZ: number}
Test1<{ ZZ: number }>('test', arg => { arg.ZZ }, m => ({ ZZ: 123 })) // obvs

另一种方法是切换回调的顺序,因为推理往往是从左到右进行的:

declare function Test2<T>(msg: string, fnTake: (arg: any) => T, fn: (arg: T) => void): void;

Test2('test', m => ({ ZZ: 123 }), arg => { arg.ZZ }); // T is {ZZ: number}

我认为相同的推理延迟发生在第一遍,但现在在第二遍编译器可以检查 m => ... 并将 any 的上下文类型用于 m然后推断 {ZZ: number} 的 return 类型,现在它有一个 T 的推断候选者,当它到达 arg => ... 回调时一切正常。


好的,希望这是有道理的,并给出了一些方向。祝你好运!

Playground link to code