TypeScript函数返回类型void与unknown可双向赋值的原因
TypeScript void返回值函数的特殊赋值规则解析
现象复现
类型检查的矛盾表现可通过以下代码复现:
// 普通变量赋值场景 /* A1 */ declare let a: void; /* A2 */ declare let b: unknown; /* A3 */ a = b; // 抛出类型错误 /* A4 */ b = a; // 无类型错误 // 函数赋值场景 /* L1 */ let f1 = (): void => { return; } /* L2 */ let f2 = (): unknown => { return; } /* L3 */ f1 = f2; // 无类型错误 /* L4 */ f2 = f1; // 无类型错误
核心原因
这个看似违背类型协变规则的表现,本质是TypeScript对返回值为void的函数类型做了非严格的特殊规则设计,没有完全套用常规的返回值协变逻辑:
- 普通变量赋值场景完全遵循类型层级规则:
void是unknown的子类型,类型范围更窄,因此子类型赋值给父类型(A4行)合法,父类型赋值给子类型(A3行)报错,符合预期。 - 常规函数返回值确实遵循协变规则:如果类型
T是类型U的子类型,那么() => T也是() => U的子类型,可以合法赋值。示例中L4行f2 = f1就完全符合这个规则:void是unknown的子类型,因此返回void的f1可以赋值给声明为返回unknown的f2。 - L3行
f1 = f2不报错,是触发了void返回值的特殊宽松校验:当赋值目标的函数类型声明返回值为void时,TypeScript不会强制要求赋值源的函数返回值必须是void的子类型,任意返回值类型的函数都可以合法赋值到这个位置。
规则设计背景
这个特殊规则是为了适配日常开发的通用写法:返回值标注为void的函数,本身就代表调用方约定不会使用该函数的返回值,因此没必要强制要求传入的函数必须真的不返回任何值。
比如数组方法Array.prototype.forEach的回调类型定义返回值为void,实际开发中我们完全可以传入有返回值的回调(比如直接传入console.log、Array.prototype.push这类本身带返回值的函数),TS不会抛出类型错误,就是这个规则的实际应用。
需要注意的是,这个宽松规则仅在函数类型的赋值场景生效,普通变量赋值给void类型时不会触发该规则,因此A3行的普通变量赋值依然会正常报错。
内容的提问来源于stack exchange,提问作者eczn
相关产品推荐
相关产品推荐

