第 99 期 - The Problem with TypeScript's Method Shorthand Syntax
logoFRONTALK AI/2月1日 16:31/阅读原文

摘要

文章对比了 TypeScript 中方法简写和对象属性两种语法,阐述方法简写语法因双变性可能导致运行时错误,建议使用对象属性语法,还提到可利用 ESLint 规则避免方法简写语法。

1. 两种语法的定义

在 TypeScript 中,对象上定义函数有两种语法方式。一种是方法简写语法,如interface Obj { version1(param: string): void; };另一种是对象属性语法,如interface Obj { version2: (param: string) => void; }。它们看起来很相似,但存在细微差别。

2. 方法简写语法导致运行时错误

2.1 以 Dog 接口为例

首先定义Dog接口,包含barkAt方法,interface Dog { barkAt(dog: Dog): void; }。然后定义SmallDog接口继承Dog接口并有额外的whimper方法。当使用Dog类型变量时,可以给barkAt方法的参数标注比Dog接口预期更窄的类型,如const brian: Dog = { barkAt(dog: SmallDog) {}, };,这里brian只想对有whimper方法的SmallDog叫。但在barkAt函数内部可能调用dog.whimper(),如果传入没有whimper方法的普通Dog,如const normalDog: Dog = { barkAt() {}, };brian.barkAt(normalDog);就会产生运行时错误。

2.2 本质原因

这是因为方法简写语法是双变的,即方法可以接受比原始类型更窄或更宽的类型,这种意外的双变性会导致运行时错误。

3. 对象属性语法的优势

如果使用对象属性语法定义方法,如interface Dog { barkAt: (dog: Dog) => void; },当试图给方法分配更窄的类型时,TypeScript 会报错。这更符合我们的预期,因为对象属性语法只接受比原始类型更窄的类型。

4. 语法与函数类型无关

一个常见的误解是上述语法与箭头函数和函数声明有关。实际上,两种语法都可以与箭头函数或函数声明一起使用。例如interface Obj { methodShorthand(param: string): void; objectProperty: (param: string) => void; },函数声明function functionDeclaration(param: string) {}和箭头函数const arrowFunction = (param: string) => {}都可以在两种语法中使用。

5. 与type的情况相同

使用type也会出现同样的问题。如type Dog = { barkAt(dog: Dog): void; }type SmallDog = { whimper: () => void; } & Dog;也会出现运行时错误。

6. 如何避免问题

可以使用 ESLint 规则@typescript-eslint/method - signature - style来避免这个问题,将规则设置为{"rules": {"@typescript-eslint/method-signature-style": ["error", "property"]}},这样如果使用方法简写语法就会报错。

 

扩展阅读

Made by 捣鼓键盘的小麦 / © 2025 Front Talk 版权所有