编译器可以推断出你的代码做了什么,但它不知道你的意图。举个简单的例子:
function thing(value: string) {
return value === "foo" ? 123 : "456";
}
推断的类型是function thing(value: string): 123 | "456",它与实现相匹配,但这是否有意义对?例如,也许我打算总是返回一个number;如果我告诉编译器,它可以告诉我我没有:
Type 'string | number' is not assignable to type 'number'.
Type 'string' is not assignable to type 'number'.(2322)
特别是当您使用复杂的包装器/泛型类型时(例如,我在 angular 中看到很多与此相关的问题,其中使用了 RxJS 可观察对象),这确实有助于获得有关您的假设的早期反馈。
我还经常使用测试驱动开发 (TDD),其中在实现之前编写测试的价值之一是它让您有机会在更改接口的成本接近时讨论接口零。使用经典的 RPS 示例:
function rps(left: string, right: string) {
return "right";
}
it("returns 'right' for 'rock' vs. 'paper'", () => {
expect(rps("rock", "paper")).to.equal("right");
});
这会编译并通过,但它是我们想要的吗?现在我们可以谈谈选项了:
-
我们是否接受推断类型function rps(left: string, right: string): string?
-
使用例如更具体
type Throw = "rock" | "paper" | "scissors";
type Outcome = "left" | "right" : "draw";
function rps(left: Throw, right: Throw): Outcome { ... }
-
改用enums?
我们可以讨论权衡取舍,并根据我们当时所知道的情况选择最佳选项。显式返回类型用作我们做出的决定的文档。
如果您要对代码进行 linting,我建议您打开 @typescript-eslint/explicit-function-return-type,这提供了它的基本原理:
函数返回值的显式类型使任何人都清楚
调用代码返回什么类型。这确保了返回值
分配给正确类型的变量;或者在这种情况下
没有返回值,调用代码不会尝试使用
不应该定义的值。