为何在MyReturnType中用unknown替代any会导致类型检查失败?
问题分析:MyReturnType用unknown[]实现时Equal泛型不返回true的原因
问题重现
实现MyReturnType时使用unknown[]作为函数参数的匹配类型:
type MyReturnType<T> = T extends (...args: unknown[]) => infer R ? R : never
测试时发现,虽然MyReturnType<(v: boolean) => string>、MyReturnType<typeof fn>、MyReturnType<typeof fn1>的类型推断结果在编辑器中显示正确,但通过Equal泛型验证时始终返回false;将unknown[]替换为any后,Equal验证正常通过。
已知Equal泛型实现:
export type Equal<X, Y> = (<T>() => T extends X ? 1 : 2) extends (<T>() => T extends Y ? 1 : 2) ? true : false
核心原因
1. unknown与any在函数类型匹配中的兼容性差异
TypeScript中函数类型的兼容性遵循参数逆变、返回值协变规则:
- 当用
(...args: unknown[]) => infer R匹配目标函数时,unknown作为严格的顶级类型,会要求目标函数的参数类型满足逆变约束(即匹配函数的参数类型需是目标函数参数类型的子类型)。虽然这种匹配能正确推断返回类型R,但类型系统会保留匹配过程中的约束痕迹,导致推断出的R与直接的预期类型(如string)在外延等价性上存在细微差异。 - 当替换为
(...args: any[]) => infer R时,any是宽容的顶级类型,会忽略函数参数的逆变约束细节,匹配过程不会留下额外的类型约束痕迹,推断出的R与预期类型在外延等价性上完全一致。
2. Equal泛型的严格性
Equal的实现基于类型的外延等价性:通过比较两个泛型函数的兼容性,判断对于所有类型T,T extends X和T extends Y的结果是否完全一致。这种判断方式对类型的细微差异极为敏感——即使R表面上和预期类型相同,unknown匹配留下的约束痕迹会导致泛型函数的兼容性检查不通过,最终返回false;而any的匹配则不会产生这类痕迹,因此检查通过。
总结
unknown的严格特性导致函数类型匹配后留下的类型约束,触发了Equal泛型的严格检查机制,使得推断出的返回类型与预期类型不被认为完全等价;而any的宽容性避免了这一问题,因此替换后验证正常。
内容的提问来源于stack exchange,提问作者ibrahim koz
相关产品推荐
相关产品推荐

