四、TypeScript概念补充

89 阅读4分钟

类型缩小

什么是类型缩小呢?

  • 类型缩小的英文是 Type Narrowing;

  • 我们可以通过类似于 typeof padding === "number" 的判断语句,来改变TypeScript的执行路径;

  • 在给定的执行路径中,我们可以缩小比声明时更小的类型,这个过程称之为 缩小;

  • 而我们编写的 typeof padding === "number 可以称之为 类型保护(type guards);

常见的类型保护有如下几种:

typeof

平等缩小(比如===、!==)

instanceof

in,等等

typeof

在 TypeScript 中,检查返回的值typeof是一种类型保护:因为 TypeScript 对如何typeof操作不同的值进行编码。


type IDType = number | string
function printID(id: IDType) {
  if (typeof id === 'string') {
    console.log(id.toUpperCase())
  } else {
    console.log(id)
  }
}

平等缩小

平等缩小是指使用 ===、==、!==、!=、switch 等运算符进行类型缩小。在给定的代码中,我们使用了 Direction 类型的值进行了平等缩小。具体来说,我们使用了 switch 运算符进行了类型缩小。


type Direction = "left" | "right" | "top" | "bottom"
function printDirection(direction: Direction) {
  switch (direction) {
    case 'left':
      console.log(direction)
      break;
    case 'right':
      console.log(direction)
      break;
    case 'top':
      console.log(direction)
      break;
    case 'bottom':
      console.log(direction)
      break;
  }
}

instanceof

在这个例子中,我们使用了 instanceof 运算符对 Student 和 Teacher 类型进行了缩小。

class Student {
  studying() {}
}

class Teacher {
  teaching() {}
}

function work(p: Student | Teacher) {
  if (p instanceof Student) {
    p.studying()
  } else {
    p.teaching()
  }
}

const stu = new Student()
work(stu)

in

在这个例子中,我们使用了 in 运算符对 Fish 和 Dog 类型进行了缩小。

type Fish = {
  swimming: () => void
}

type Dog = {
  running: () => void
}

function walk(animal: Fish | Dog) {
  if ('swimming' in animal) {
    animal.swimming()
  } else {
    animal.running()
  }
}

const fish: Fish = {
  swimming() {
    console.log("swimming")
  }
}

walk(fish)

函数类型

在 TypeScript 中, 函数也是一种类型, 可以像其它类型一样被使用.

函数类型的定义

函数类型的定义包括两部分: 参数类型和返回值类型.

例如, 下面是一个函数类型的定义:

type AddFnType = (num1: number, num2: number) => number

函数类型的应用

在 TypeScript 中, 可以使用 type 关键字定义一个函数类型, 然后使用这个类型定义常量.例如, 在以下代码中,  AddFnType 是一个函数类型, add 是一个常量, 它的类型是 AddFnType

type AddFnType = (num1: number, num2: number) => number
const add: AddFnType = (a1: number, a2: number) => {
  return a1 + a2
}

参数的默认值

从ES6开始,JavaScript是支持默认参数的,TypeScript也是支持默认参数的

在 TypeScript 中, 函数的参数可以设置默认值.

例如, 下面的代码中, name参数的默认值是 'world':

function sayHello(name = 'world') {
  console.log(`Hello, ${name}!`)
}

sayHello() // 输出: Hello, world!
sayHello('TypeScript') // 输出: Hello, TypeScript!

如果调用 sayHello函数时不传入参数, 那么 name参数的值就是 'world'. 如果传入了参数, 那么  name参数的值就是传入的参数.

注意, 如果一个参数设置了默认值, 那么它就变成了可选参数.也就是说, 在调用函数时可以不传入这个参数.

例如, 上面的代码中, name参数设置了默认值, 所以在调用sayHello函数时可以不传入这个参数。

如果不想让一个参数成为可选参数, 可以将它的默认值设置为undefined.例如, 下面的代码中, name参数的默认值是 undefined, 所以在调用 sayHello函数时必须传入这个参数:

function sayHello(name: string = undefined) {
  console.log(`Hello, ${name}!`)
}

sayHello('TypeScript') // 输出: Hello, TypeScript!
sayHello() // Error: Expected 1 arguments, but got 0.

剩余参数

image.png

在 TypeScript 中, 可以使用 rest 参数语法将这些参数整合成一个数组.console.log会把我们传入的参数放到一个数组里进行操作。

function log(message: string, ...optionalParams: any[]) {
  console.log(message, ...optionalParams)
}

log('Hello, TypeScript!', 10, true, 'foo')

function sum(initalNum: number, ...nums: number[]) {
  let total = initalNum
  for (const num of nums) {
    total += num
  }
  return total
}

console.log(sum(20, 30))
console.log(sum(20, 30, 40))
console.log(sum(20, 30, 40, 50))

函数重载

使用联合类型实现不同的函数:

function add(a1: number | string, a2: number | string) {
  if (typeof a1 === "number" && typeof a2 === "number") {
    return a1 + a2
  } else if (typeof a1 === "string" && typeof a2 === "string") {
    return a1 + a2
  }

  // return a1 + a2;
}

add(10, 20)

使用联合类型进行逻辑判断的缺点有以下两个:

1.代码可读性差

使用联合类型进行逻辑判断时,需要进行很多的类型缩小操作,这样会导致代码可读性变差,增加代码的维护成本。

2.返回值类型不确定

使用联合类型进行逻辑判断时,函数的返回值类型依然是不能确定的,这样会导致代码的可靠性变差,增加代码的调试成本。

为了解决这两个问题,我们可以使用函数重载来实现函数的多态性,这样既可以提高代码的可读性,又可以确定函数的返回值类型。

function add(a1: number, a2: number): number;
function add(a1: string, a2: string): string;
function add(a1: any, a2: any): any {
  if (typeof a1 === "number" && typeof a2 === "number") {
    return a1 + a2
  } else if (typeof a1 === "string" && typeof a2 === "string") {
    return a1 + a2
  }
}
add(10, 20) // 返回30
add("hello", "world") // 返回"helloworld"

function add(a1: any, a2: any): any这个函数是不能单独调用的比如add(0,"1"),因为唯一调用的两种方法都声明了function add(a1: number, a2: number): number;function add(a1: string, a2: string): string;只能同时传入两个number或者两个string.