TS 内置类型和工具类型

848 阅读4分钟

「这是我参与2022首次更文挑战的第24天,活动详情查看:2022首次更文挑战」。

JavaScript 中有很多内置对象,它们可以直接在 TypeScript 中当做定义好了的类型。

内置对象是指根据标准在全局作用域 global 上存在的对象,这里的标准指的是 ECMAcript 和其他环境(比如DOM)的标准。

内置类型

ECMAScript 的内置对象

比如,ArrayDateError 等,

const nums: Array<number> = [1,2,3]

const date: Date = new Date()

const err: Error = new Error('Error!');

const reg: RegExp = /abc/;

Math.pow(2, 9)

Array 为例,按住 comand/ctrl,再鼠标左键点击一下,就能跳转到类型声明的地方。

image.png

可以看到,Array 这个类型是用 interface 定义的,有多个不同版本的 .d.ts 文件声明了这个类型。

在 TS 中,重复声明一个 interface,会把所有的声明全部合并,这里所有的 .d.ts 文件合并出来的 Array 接口,就组合成了 Array 内置类型的全部属性和功能。

DOM 和 BOM

比如 HTMLElementNodeListMouseEvent

let body: HTMLElement = document.body

let allDiv: NodeList = document.querySelectorAll('div');

document.addEventListener('click', (e: MouseEvent) => {
    e.preventDefault()
  // Do something
});

工具类型(Utility Types)

为了方便开发者使用, TypeScript 内置了一些常用的工具类型,比如 typeofextendskeyof 等,这里介绍几个,起一个抛砖引玉的作用,更多可参考 Ts高手篇:22个示例深入讲解Ts最晦涩难懂的高级类型工具

毕竟工具函数遇到了去查就行,死记硬背就太枯燥了,熟能生巧。

typeof

typeof 操作符可以用来获取一个变量声明的类型。

之前介绍 类型保护 的时候,就已经讲过 typeof 操作符了,我们来复习一下,

function getLength(arg: number | string): number {
    if(typeof arg === 'string') {
        return arg.length
    } else {
        return arg.toString().length
    }
}

extends

extends 用于 约束泛型,之前讲过,复习一下,

interface ILength {
    length: number
}

function printLength<T extends ILength>(arg: T): T {
    console.log(arg.length)
    return arg
}

这样入参就一定要有 length 属性,比如 str、arr、obj 都可以, num 就不行。

const str = printLength('lin')
const arr = printLength([1,2,3])
const obj = printLength({ length: 10 })

const num = printLength(10) // 报错,Argument of type 'number' is not assignable to parameter of type 'ILength'

keyof

keyof 操作符可以用于获取某种类型的所有键,其返回类型是联合类型。

interface IPerson {
  name: string;
  age: number;
}

type Test = keyof IPerson; // 'name' | 'age'

上面的例子,Test 类型变成了一个字符串字面量。

之前介绍 泛型约束后端入参 的时候,介绍过 keyof,复习一下,

interface API {
    '/book/detail': {
        id: number,
    },
    '/book/comment': {
        id: number
        comment: string
    }
    ...
}


function request<T extends keyof API>(url: T, obj: API[T]) {
    return axios.post(url, obj)
}

in

in 用来实现遍历

type Person = "name" | "school" | "major"

type Obj =  {
  [p in Person]: string
}

image.png

Partial

Partial<T>T的所有属性变成可选的,例如:

interface IPerson {
    name: string
    age: number
}

let p1: IPerson = {
    name: 'lin',
    age: 18
}

使用了 IPerson 接口,就一定要传 name 和 age 属性,

image.png

使用 Partial 改造一下,就可以变成可选属性,

interface IPerson {
    name: string
    age: number
}

type IPartial = Partial<IPerson>

let p1: IPartial = {}

Partial原理分析

Partial 的实现用到了上文提到的 inkeyof

type Partial<T> = {
  [P in keyof T]?: T[P];
}
  • [P in keyof T]遍历T上的所有属性
  • ?:设置为属性为可选的
  • T[P]设置类型为原来的类型 看似高大上的东西其实这么简单,其他的一些工具类型(比如 Pick、Record、Exclude 等)其实也很简单,只要去分析过,就会发现泛型的写法和 JS 的语法非常类似,只是把传参的变量换成了类型而已。

Omit

Omit<T, U>从类型 T 中剔除 U 中的所有属性。

interface IPerson {
    name: string
    age: number
}

type IOmit = Omit<IPerson, 'age'>

这样就剔除了 IPerson 上的 age 属性。

image.png

小结

本文提到的这些内置类型,全都定义在TypeScript 核心库的定义文件,这些文件包含了es2015 到 es2022 的全部 JS 语法,这里也应证了那句话——TS 是 JS 的超集。

至于工具类型,知道一些常用的,其他的遇到了再去查就行,不必死记硬背,即使忘了某个工具类型是怎么用的,去查一下很快就熟悉了,写多了也会慢慢熟悉的。

往期

轻松拿下 TS 泛型

TS 中 interface 和 type 究竟有什么区别?

TS 高级类型

TS 类型推论

TS 枚举类型

通俗易懂的 TS 基础知识总结