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 => ...
回调时一切正常。
好的,希望这是有道理的,并给出了一些方向。祝你好运!
目标:
我希望它使用第二次回调中的 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 => ...
回调时一切正常。
好的,希望这是有道理的,并给出了一些方向。祝你好运!