这目前有效(TS 3.7),但 TS 团队并没有故意允许它,也没有决定支持它。它可能会在未来的版本中中断。
type FinalReturnType<T> = {
0: T,
1: T extends (...args: any) => infer R ? FinalReturnType<R> : T,
}[T extends (...args: any) => infer _ ? 1 : 0];
它必须如此奇怪和丑陋,因为目前有一个限制,即一个类型只能在嵌套在另一个结构类型(在这种情况下,一个具有两个键的对象)中时才能调用自身,但随后我们立即拉取该属性我们需要元组。
理想的外观:
如果语言支持这种机制,我认为它会很简单:
type FinalReturnType<T> = T extends (...args: any) => infer R
? FinalReturnType<R>
: T;
换句话说,使用递归定义。如果T 是一个函数,它会调用自己并传递函数的返回类型。如果不是函数,则返回 T 本身。比如:
FinalReturnType<string> // -> string
这样就终止了递归:当你解开所有返回函数的函数层时,你最终会到达一个非函数。
很遗憾,这是不可能的,会产生错误:
Type alias 'FinalReturnType' circularly references itself.
有一个change to enable recursive type references,但它不支持这种情况。
有an open discussion of more general support for recursion。
目前的情况是,只有在自调用周围加上[]使其成为元组,才能编译我的答案:
type FinalReturnType<T> = T extends (...args: any) => infer R
? [FinalReturnType<R>]
: T;
type t1 = FinalReturnType<string>; // -> string
type F = () => string
type t2 = FinalReturnType<F>; // -> [string]
type FF = () => () => string
type t3 = FinalReturnType<FF>; // -> [[string]]
如您所见,这将结果重新包装在嵌套元组的层中,您又回到了同样的问题,因为您也无法解开这些。
目前,您目前可以使用包装在具有两个属性的对象中的技巧,然后通过索引正确的属性立即展开,如本答案顶部所述。但这是偶然启用的,TS 团队不确定他们是否愿意支持/鼓励这一点。