TypeScript箭头函数两种声明方式的差异及类型校验问题
TypeScript箭头函数声明的类型检查差异解析
问题重现
先看代码示例:
type MyState = { required: boolean; optional?: boolean; }; const myFn1: (s: MyState) => MyState = (s) => ({...s, unexpected: 1}) // 无错误 const myFn2 = (s: MyState): MyState => ({...s, unexpected: 1}) // 报错
myFn2的错误信息:
Type '{ unexpected: number; required: boolean; optional?: boolean | undefined; }' is not assignable to type 'MyState'. Object literal may only specify known properties, and 'unexpected' does not exist in type 'MyState'.
两种写法的核心区别
myFn2报错原因
myFn2是直接给函数的返回值指定了MyState类型,返回的是对象字面量。TypeScript对直接赋值给类型的对象字面量会执行超额属性检查——只要字面量包含类型中未定义的属性,就会抛出错误,这是TS的一种保护机制,防止不小心添加多余属性。
myFn1不报错原因
myFn1是先给变量指定了完整的函数类型(s: MyState) => MyState,再把函数赋值给它。此时TS检查的是函数类型的兼容性:
- 右侧函数的返回值类型是
{ unexpected: number; required: boolean; optional?: boolean },这个类型是MyState的子类型(包含MyState的所有必填/可选属性)。 - TS中函数的返回值是协变的,子类型可以安全赋值给父类型,因此不会触发超额属性检查,所以不会报错。
如何定义FnType让myFn3报错
要让const myFn3: FnType = (s) => ({...s, unexpected: 1})触发和myFn2一样的错误,需要让FnType的返回类型严格禁止额外属性,要求返回值精确匹配MyState。可以通过以下两种方式实现:
方法1:使用通用精确类型工具
// 定义通用的精确类型约束工具 type Exact<T> = T & Record<Exclude<keyof T, keyof T>, never>; // 定义目标FnType type FnType = (s: MyState) => Exact<MyState>;
方法2:直接添加额外属性禁止规则
type FnType = (s: MyState) => MyState & { [key: string]: never };
这种方式通过{ [key: string]: never }禁止任何额外的字符串属性,当返回对象包含unexpected这类多余属性时,number类型无法赋值给never类型,就会触发错误。
内容的提问来源于stack exchange,提问作者Noob Life
相关产品推荐
相关产品推荐

