Skip to content
Published at:

Narrowing

TS的变量可能是多种类型,需要确定类型,在去做相应的处理

ts
function padLeft(padding: number | string, input: string): string {
  return ' '.repeat(padding) + input; 
  // Error: Argument of type 'string | number' is not assignable to parameter of type 'number'.
  //          Type 'string' is not assignable to type 'number'.
}

改进后

ts
function padLeft(padding: number | string, input: string): string {
  if (typeof padding === 'number') {
    return ' '.repeat(padding) + input;
  }
  return padding + input;
}

typeof type guards

typeof来判断基本类型,返回对应类型的字符串

  • "string"
  • "number"
  • "bigint"
  • "boolean"
  • "symbol"
  • "undefined"
  • "object"
  • "function"

Truthiness narrowing

下面都是 false:

  • 0
  • NaN
  • "" (the empty string)
  • 0n (the bigint version of zero)
  • null
  • undefined

Equality narrowing

switch语句和相等比较(===, !==, ==, 和 != )会缩窄类型,保证类型相同; 下面示例,if判断会保证x和y的类型相同,所以当x === y为 true 时,x和y的类型都是 string

ts
function example(x: string | number, y: string | boolean) {
  if (x === y) {
    // We can now call any 'string' method on 'x' or 'y'.
    // (method) String.toUpperCase(): string
    x.toUpperCase();
    // (method) String.toLowerCase(): string
    y.toLowerCase();
  } else {
    // (parameter) x: string | number
    console.log(x);
    // (parameter) y: string | boolean
    console.log(y);
  }
}

The in operator narrowing

in用来判断是否是Object里面的成员

ts
type Fish = { swim: () => void };
type Bird = { fly: () => void };

function move(animal: Fish | Bird) {
  if ('swim' in animal) {
    return animal.swim();
  }
  return animal.fly();
}

instanceof narrowing

instanceof用来判断是否某个类型的实例

ts
function logValue(x: Date | string) {
  if (x instanceof Date) {
    // (parameter) x: Date
    console.log(x.toUTCString());
  } else {
    // (parameter) x: string
    console.log(x.toUpperCase());
  }
}

Assignments

赋值符号=会去缩窄类型

ts
let x = Math.random() < 0.5 ? 10 : 'hello world!'; // let x: string | number;

x = 1; // let x: number;
console.log(x);

x = 'goodbye!'; // let x: string
console.log(x);

Control flow analysis

Using type predicates

pet is Fish is our type predicate in this example

ts
type Fish = { swim: () => void };
type Bird = { fly: () => void };

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

let pet = getSmallPet();

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

Assertion functions

Discriminated unions

用联合类型的成员来告知 Object 当前是什么类型

ts
interface Shape {
  kind: 'circle' | 'square';
  radius?: number;
  sideLength?: number;
}

function handleShape(shape: Shape) {
  // oops!
  if (shape.kind === 'rect') { 
    // Error: This comparison appears to be unintentional because the types '"circle" | "square"' and '"rect"' have no overlap.
    // ...
  }
}

// 配合non-null断言使用`!`
function getArea(shape: Shape) {
  if (shape.kind === 'circle') {
    return Math.PI * shape.radius! ** 2;
  }
}

另一种方式:联合类型 + 多Interface

ts
interface Circle {
  kind: 'circle';
  radius: number;
}
interface Square {
  kind: 'square';
  sideLength: number;
}
type Shape = Circle | Square;

The never type

// TODO

Exhaustiveness checking

// TODO