TypeScript中ReturnType工具类型的兜底any为何不能替换为never?
关于ReturnType自定义实现中any与never兜底的疑问解答
一、官方实现里的兜底any什么时候会触发?
官方文档里ReturnType常见的自定义实现是这样的:
type ReturnType<T extends (...args: any[]) => any> = T extends (...args: any[]) => infer R ? R : any;
这里的any兜底分支正常使用时几乎不会触发,因为前面的泛型约束T extends (...args: any[]) => any已经明确要求传入的T必须是函数类型。只要你传的不是函数,TypeScript在编译阶段直接就会报错,根本轮不到走else分支返回any。
只有在一些极端的、刻意绕过约束的场景下才会触发:
- 比如用类型断言硬塞非函数类型:
ReturnType<{} as any>,此时any能绕过泛型约束,但{}不是函数结构,条件判断不成立,就会返回兜底的any。 - 或者关闭了严格模式(比如
--noImplicitAny),传入了一些边缘类型,但这种情况非常少见。
二、为什么不能替换成never?
其实绝大多数常规场景下,换成never完全没问题,甚至像type-challenges这类社区挑战里,很多开发者都会用never兜底——毕竟不符合要求的输入就该返回一个无效类型,never刚好能表达“这个类型根本不存在有效值”的语义。
官方坚持用any的原因大概有两点:
- 兼容老旧代码:早期TypeScript的类型处理逻辑和现在不同,
any作为兜底能避免一些老代码里的意外报错,保证兼容性。 - 容错性更高:要是有人不小心用
@ts-ignore或者断言绕过了约束,返回any不会让后续代码直接出现类型错误;但never的特性是无法被赋值、调用,后续使用这个返回值时会直接报错,反而引发更多问题。
三、实际开发中该怎么选?
- 如果你的代码是严格模式,不会刻意绕过泛型约束,选
never更合理,语义更精准。 - 如果要兼容非严格模式的老旧代码,或者担心有人乱绕约束导致报错,保留
any兜底更稳妥。
内容的提问来源于stack exchange,提问作者brandonwie
相关产品推荐
相关产品推荐

