3 min read
TypeScript关键字is

起源是在 ts 项目里有描述 Filter 返回类型的需求, 省略后的代码如下

type Item = { type: Type.A };

enum Type {
  A,
  B,
}

const foo = <T extends Item>(arr: T[]) => {
  return arr;
};

const arr: { type: Type }[] = [
  {
    type: Type.A,
  },
  {
    type: Type.B,
  },
];

const result = foo(arr.filter<Item>((item) => item.type === Type.A));

由于 foo 的入参只接受 arr 过滤后的某种类型,默认情况下 ts 会报错:

error1

error2

于是我开始查看 Array 的 filter 声明描述:

interface Array<T> {
  // ...
  /**
   * Returns the elements of an array that meet the condition specified in a callback function.
   * @param predicate A function that accepts up to three arguments. The filter method calls the predicate function one time for each element in the array.
   * @param thisArg An object to which the this keyword can refer in the predicate function. If thisArg is omitted, undefined is used as the this value.
   */
  filter<S extends T>(
    predicate: (value: T, index: number, array: T[]) => value is S,
    thisArg?: any
  ): S[];
  /**
   * Returns the elements of an array that meet the condition specified in a callback function.
   * @param predicate A function that accepts up to three arguments. The filter method calls the predicate function one time for each element in the array.
   * @param thisArg An object to which the this keyword can refer in the predicate function. If thisArg is omitted, undefined is used as the this value.
   */
  filter(
    predicate: (value: T, index: number, array: T[]) => unknown,
    thisArg?: any
  ): T[];
}

什么是 is 关键字?

is 是 TypeScript 语法里的类型谓词,表示是否属于某个类型,可以有效地缩小类型范围。

使用类型谓词

A predicate takes the form parameterName is Type, where parameterName must be the name of a parameter from the current function signature.1

function isFish(pet: Fish | Bird): pet is Fish {
  return (pet as Fish).swim !== undefined;
}

类型谓词可以在条件语句中识别:

// Both calls to 'swim' and 'fly' are now okay.
let pet = getSmallPet();

if (isFish(pet)) {
  pet.swim();
} else {
  pet.fly();
}

我发现,在使用typeof是做类型判断时,ts 会自动识别返回的类型,无需使用类型谓词

最后,回到我的例子里补充了类型谓词后的代码则是:

type Item = { type: Type.A };

enum Type {
  A,
  B,
}

const foo = <T extends Item>(arr: T[]) => {
  return arr;
};

const arr: { type: Type }[] = [
  {
    type: Type.A,
  },
  {
    type: Type.B,
  },
];

const result = foo(
  arr.filter<Item>((item): item is Item => item.type === Type.A)
);

分享一下 TypeScript 官网的速查表2,可以帮助快速查找常见 TypeScript 代码的语法:

TypeScript Control Flow Analysis

Footnotes

  1. Using type predicates

  2. TypeScript 速查表