Effective TypeScript

使用类型推导避免你的代码变糟糕

许多从 JS 转到 TS 的开发者第一件事就是将所有的代码都补上类型注解,其实很多情况下是没有必要的。

let x: number = 12;

就可以直接写成

let  = 12;

TS 会自动推导 x 的类型,可以看见,当前 x 的类型被推导为 number,是正确的。

即使是复杂的对象,TS 也能准确的完成其类型的推导。

const  = {
  : "Sojourner Truth",
  : {
    : "Swartekill, NY",
    : "c.1797",
  },
  : {
    : "Battle Creek, MI",
    : "Nov. 26, 1883",
  },
};

可以看见 person 也能过正确完成推导。

有些时候,TS 所进行的推导甚至比你自己以为的类型更为精确:

const axis1: string = "x";
const axis1: string
const axis2 = "y";
const axis2: "y"

对于 axis1 来说,string 类型并没有 axis2 所得到的推导类型 "y" 更为精准。

类型推导对于代码重构也更为方便,设想如下场景:

interface Product {
  id: number;
  name: string;
  price: number;
}

function logProduct(product: Product) {
  const id: number = product.id;
  const name: string = product.name;
  const price: number = product.price;
  console.log(id, name, price);
}

你定义了一个 Product 类型,并且在使用该类型数据时,取 id 数据时,你显式声明了变量的类型。

但是后来因为某些原因(可能是需求变更),id 的类型变为了 string,你可能会直接将 Product 定义中 id 的类型直接更改为 string,此时,logProduct 函数就会报错。

// @errors: 2552
interface Product {
  id: string;
  name: string;
  price: number;
}

function logProduct(product: Product) {
  const id: number = product.id;
  const name: string = product.name;
  const price: number = product.price;
  console.log(id, name, price);
}

但如果你在写函数 logProduct 时,不显式指定 id 的类型,而是让 TS 去完成类型推导,那么此时就不需要再去修改 logProduct 的代码。

interface Product {
  id: string;
  name: string;
  price: number;
}

function logProduct(product: Product) {
  const { id, name, price } = product;
  console.log(id, name, price);
}

当然,显式类型注解并非一无是处,在 TS 无法获取足够上下文信息从而正确完成类型推导时,类型注解就显得尤为重要,例如在定义函数参数类型时就是如此。

函数签名包含类型注解,函数体局部变量无需类型注解,此为理想的 TS 代码。

对于具有默认值的函数参数可以不使用类型注解,因为 TS 能从默认值推导其类型:

function (: string, base = 10) {
base: number
// ... }

在使用回调函数时,函数参数类型可以自动推导。

在某些情况下,即使类型能够正确推导,但加上类型注解,在错误出现时,TS 能够更加精准地定位。

  1. 当使用字面量定义对象时
const elmo: Product = {
  name: "Tickle Me Elmo",
  id: "048188 627152",
  price: 28.99,
};
interface Product {
  id: string;
  name: string;
  price: number;
}

function logProduct(product: Product) {
  const { id, name, price } = product;
  console.log(id, name, price);
}

const furby = {
  name: "Furby",
  id: 630509430963,
  price: 35,
};

logProduct(furby);
const furby: Product = {
  name: "Furby",
  id: 630509430963,
  price: 35,
};
logProduct(furby);
  1. 函数返回类型

显式定义函数返回类型后,对于具有多个 return 语句的函数来说,TS 能够对返回的类型是否一致作出判断。

另外,将函数返回类型和输入类型明确写出,对于你来说就像是给函数绘制了一份草图。

interface Vector2D {
  : number;
  : number;
}
function add(: Vector2D, : Vector2D) {
function add(a: Vector2D, b: Vector2D): {
    x: number;
    y: number;
}
return { : . + ., : . + . }; }

使用命名类型作为返回类型时,返回值可能不会推导为你所想要的类型,因此此时主动确认返回类型更好。

最后,标注返回类型后,TS 所需要做的推导工作会变少,这对大型项目来说具有很大的影响。

linter 的规则: no-inferrable-types 能够保证你代码中的类型注解是必要的(没有多余的)。

在 GitHub 上编辑

上次更新于