typescript instanceof typescript typeof返回的类型

在 TypeScript 中,当处理函数交叉类型时,其行为直接于函数重载。然而,在实际调用此类函数时,TypeScript 会根据参数匹配度选择最合适的(通常是第一个)签名来确定返回类型;而在使用 `infer` 时进行类型推断时,它却倾向于从最后一个函数签名进行推断,这导致了返回类型的不一致。本文将深入探讨这一现象,并提供行为重构建议,以确保类型推断的准确性和一致性。在TypeScript中理解函数交叉类型与函数重载
,函数交叉类型(函数的交叉类型)的与函数重载(函数重载)非常相似。一个由多个函数类型通过amp;符号连接而成的交叉类型,会被TypeScript解释一个具有多个调用签名(Call Signatures)的重载函数。
以下示例:type Foo = (() =gt; Promiselt;stringgt;) amp; (() =gt; Promiselt;anygt;);type Foo2 = (() =gt; Promiselt;anygt;) amp; (() =gt; Promiselt;stringgt;);登录后复制
在这里,Foo 和 Foo2 都被视为具有两个调用签名的重载函数。关键在于,TypeScript处理重载函数时,在调用和类型推断方面存在不同的行为模式。调用时的返回类型解析
当您实际调用一个重载函数时,TypeScript 会根据确定的参数类型,选择“最合适”的调用签名来确定函数的返回类型。在没有参数或参数类型相同的情况下,通常会选择第三个匹配的签名。
例如:// 示例1: Foo 类型type Foo = (() =gt; Promiselt;stringgt;) amp; (() =gt; Promiselt;anygt;);const a: Foo = async () =gt; { return quot;quot;;};const b = wait a();// 这是实际调用时,b 的类型被推断为 string//因为 (() =gt; Promiselt;stringgt;) 是第一个签名,且匹配成功。// ^? const b: string登录后复制
在 Foo 类型中,(() =gt; Promiselt;stringgt;) 是第一个签名。因此,当 a 被调用时,其返回类型被解析为 Promiselt;stringgt;,解包后 b 的类型为 string。// 示例2: Foo2 类型type Foo2 = (() =gt; Promiselt;anygt;) amp; (() =gt;) amp; (() =gt; Promiselt;stringgt;);const c: Foo2 = async () =gt; { return quot;quot;;};const d = wait c();//实际调用时,d 的类型被推断为 any//因为这是 (() =gt; Promiselt;anygt;) 是第一个签名,且匹配成功。
// ^? const d:any登录后复制
类似地,在 Foo2 类型中,(() =gt; Promiselt;anygt;) 是第一个签名。因此,当 c 被调用时,返回类型被解析为 Promiselt;anygt;,解包后 d 的类型为 any。使用 infer 进行类型推断时的行为行为不同其,当您尝试使用类型中的 infer 时关键字(例如通过 ReturnType 工具类型)从一个重载函数类型中提取返回类型时,TypeScript 通常会从最后一个调用签名进行推断。这是一个已知的 TypeScript 设计限制。
继续上面的示例:// 示例1: Foo 类型type Foo = (() =gt; Promiselt;stringgt;) amp; (() =gt; Promiselt;anygt;);type FooResult = Foo extends () =gt; Promiselt;推断Tgt; ? T : null;// 使用推断时,FooResult 的类型被推断为 any//因为 (() =gt; Promiselt;anygt;) 是 Foo 类型中的最后一个签名。// ^? type FooResult = any 登录后复制
这里,FooResult 被推断为 any,与 b 的实际类型 string 产生了不匹配。 文心大模型
百度飞桨-文心大模型 ERNIE 3.0 文本理解与创作 56 查看详情 // 文本2: Foo2 类型type Foo2 = (() =gt; Promiselt;anygt;) amp; (() =gt; Promiselt;stringgt;);type FooResult2 = Foo2 extends () =gt; Promiselt;推断 Tgt; ?这是 T : null;// 使用推断时,FooResult2 的类型被推断为 string //因为 (() =gt; Promiselt;stringgt;) 是 Foo2 类型中的最后一个签名。
// ^? type FooResult2 = string登录后复制
同样,FooResult2被推断为string,与d的实际类型any了不匹配。
调用时取第一个签名、推断时取最后一个签名这种行为,也是导致类型不一致的根本原因。解决方案与重构建议
为了避免这种不一致性,并确保类型推断的准确性,最佳实践是尽量避免使用参数类型不同的函数交叉类型来模拟重载。如果您的函数没有不同的参数签名来不同的重载,那么它可能不适合作为重载函数处理。1. 使用显式的函数签名
如果您的目标是获得一个显式的返回类型,并且函数没有真正的重载逻辑(即不同的参数导致不同的)行为,那么应该直接使用一个单一的函数签名来表示其类型。
例如,如果您希望函数始终返回 Promiselt;stringgt;,则应定义简单:type MyFunctionType = () =gt;Promiselt;stringgt;;const myFunc: MyFunctionType = async () =gt; { return quot;helloquot;;};type MyFuncResult = MyFunctionType extends () =gt; Promiselt;推断 Tgt; ? T : null;// ^? type MyFuncResult = stringconst result = wait myFunc();// ^? const result: string登录后复制
这样,MyFuncResult 和 result 的类型将保持一致。2. 显式定义交叉返回类型
如果您确实希望函数的返回类型是多个类型的交叉,那么不应该通过交叉函数类型来实现,而是直接在单一的函数签名中定义一个交叉的返回类型。
// 错误的示例:尝试通过函数交叉类型获得交叉返回类型type BadAttempt = (() =gt; { a: string }) amp; (() =gt; { b: number });type BadRet = ReturnTypelt;BadAttemptgt;;// ^? type BadRet = { b: number; } //唯一推断出最后一个签名 const badCall = ((): BadAttempt =gt; { return { a: quot;quot;, b: 1 };})();// ^? const badCall: { a: string; } // 实际调用得到第一个签名//正确的结果:直接在单一函数签名中定义交叉返回类型type CorrectFunction = () =gt; { a: string } amp; { b: number };const CorrectFunc: CorrectFunction = () =gt; { return { a: quot;quot;, b: 1 };};type CorrectRet = ReturnTypelt;typeof正确函数;;// ^? type CorrectRet = { a: 字符串; } amp; { b: 数字; }const CorrectCall = CorrectFunc();// ^? const CorrectCall: { a: string; } amp; { b: number; } 登录后复制
通过这种方式,CorrectRet 和 CorrectCall 的类型将完全一致,都反映了 string 和 number 的交叉类型。总结与最佳实践函数交叉类型即重载: (() =gt; T) amp; (() =gt; U) 直接于一个重载函数,其行为受 TypeScript重载规则的约束。调用与推断的不一致:调用重载函数时通常匹配第一个签名;使用推断时通常匹配最后一个签名。导致类型不一致的核心原因。避免无意义的重载:如果您的函数没有通过参数类型区分的真正重载逻辑,应避免使用函数交叉类型。使用简单明确的签名:大多数情况下,直接使用简单一个函数签名来定义其输入和输出类型,是确保类型是一致性和可预测性的最佳方法。显式交叉返回类型: 如果需要一个包含多个属性的交叉返回类型,请直接在函数的返回类型中声明这个交叉类型,而不是通过交叉函数类型来间接实现。
通过遵循这些原则,您可以有效地管理 TypeScript 中的函数类型,避免由重载和推断行为差异导致的潜在类型问题,从而编写出更健壮、更易于理解的代码。
以上就是TypeScript函数交叉类型与返回类型推断:深入理解与解决方案的内容详细,更多请关注乐哥常识网其他相关! RequestInit 类型如何为 VSCode 配置最前沿的 JavaScript/TypeScript 开发环境,包括 Lint 和更新?
