ts学习心得: 内置类型

497 阅读6分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第16天,点击查看活动详情

当你安装 TypeScript 时,会顺带安装一个 lib.d.ts 声明文件。这个文件包含 JavaScript 以及 DOM 的类型声明,以及一些内置的工具类型。它自动包含在 TypeScript 项目的编译上下文中,它能让你快速开始书写经过类型检查的 JavaScript 代码。

const foo = 123; 
const bar = foo.toString();

这段代码类型检查正常,同时能获取toString方法的类型,因为 lib.d.ts 为所有 JavaScript 对象定义了 toString 方法。

image.png

ts 内置的类型包括如下几种:

全局的类型声明

const a: Array<string> = ['1', '2']

image.png

const date = new Date()
date.getTime()

以上类型都是javascript的全局类型声明,在安装ts的时候已经内置了,不需要我们自己安装或者定义。

像这种全局的类型声明,可以理解为构造器声明类型:

  • 字符串构造函数类型:StringConstuctor
  • 数字构造函数类型:NumberConstuctor
  • 对象构造函数类型: ObjectConstuctor
  • 数组构造函数类型:ArrayConstructor ...

我们来分析StringConstuctor下的类型声明

interface StringConstructor {
    new(value?: any): String;
    (value?: any): string;
    readonly prototype: String;
    fromCharCode(...codes: number[]): string;
}

StringConstuctor 是一个构造函数,所以必须用new(new(value?: any): String;),它返回一个 String类型,那这个 String 接口是怎么样的呢?

interface String {
    toString(): string;
    charAt(pos: number): string;
    charCodeAt(index: number): number;
    concat(...strings: string[]): string;
    indexOf(searchString: string, position?: number): number;
    lastIndexOf(searchString: string, position?: number): number;
    localeCompare(that: string): number;
    match(regexp: string | RegExp): RegExpMatchArray | null;
    replace(searchValue: string | RegExp, replaceValue: string): string;
    search(regexp: string | RegExp): number;
    slice(start?: number, end?: number): string;
    split(separator: string | RegExp, limit?: number): string[];
    substring(start: number, end?: number): string;
    toLowerCase(): string;
    toLocaleLowerCase(locales?: string | string[]): string;
    toUpperCase(): string;
    toLocaleUpperCase(locales?: string | string[]): string;
    trim(): string;
    readonly length: number;
    substr(from: number, length?: number): string;
    valueOf(): string;
    readonly [index: number]: string;
}

可以看到在String接口中定义了所有的字符串方法。

内置对象

Math.pow(2, 2)

Math 与其他全局对象不同的是它不是一个构造函数,它的所有属性和方法都是静态的。

DOM 和 Event

DOM 类型声明

在Vue项目中通过template ref获取一个dom节点的时候,就会用到HTMLElement类型声明。

const fileInput = ref<null | HTMLInputElement>(null)

那么这个HTMLInputElement类型是从那来的呢?就是从内置的lib.d.ts文件中来的,我们可以看看它的声明:

interface HTMLInputElement extends HTMLElement {
  ...
  autocomplete: string;
  capture: string;
   .......
  stepUp(n?: number): void;
  addEventListener<K extends keyof HTMLElementEventMap>(...) => ...
  removeEventListener(...): void;
}

declare var HTMLInputElement: {
  prototype: HTMLInputElement;
  new(): HTMLInputElement;
};

我们可以看到首先声明了一个接口HTMLInputElement,这个接口里面就定义了input这个dom对象上的属性和方法,同时这个对象继承HTMLElement:

interface HTMLElement extends Element {
  ......
  draggable: boolean;
  hidden: boolean;
  innerText: string;
  lang: string;
  ......
  attachInternals(): ElementInternals;
  click(): void;
  addEventListener<K extends keyof HTMLElementEventMap>(...) => void;
  removeEventListener(...): void;
}

declare var HTMLElement: {
  prototype: HTMLElement;
  new(): HTMLElement;
};

image.png

这个接口又继承自Element,Node, EventTarget,我们来看看EventTarget是什么?

  • 是一个最基本的 DOM 接口
  • 可以接受事件,创建监听器等实现
interface EventTarget {
  addEventListener(
    type: string,
    callback: EventListenerOrEventListenerObject | null,
    options?: AddEventListenerOptions | boolean
  ): void
  dispatchEvent(event: Event): boolean
  removeEventListener(
    type: string,
    callback: EventListenerOrEventListenerObject | null,
    options?: EventListenerOptions | boolean
  ): void
}

根据上面的分析,EventTarget 是最基础的一个父类,当我们获取一个dom实例的时候,就可以利用类型断言来断言出具体的子类如:inputDom as HTMLInputElement

另外,任何一个dom元素都可以调用addEventListener这个方法了,因为它是继承EventTarget。

Event 类型声明

在开发中,我们经常要处理事件对象 e,那么这个e是什么类型呢?

在vue中开发上传组件时,使用拖拽功能的时候会用到 DragEvent 这个类型

const handleDrag = (e: DragEvent) => {
  e.preventDefault()
}

// 类型声明文件
interface DragEvent extends MouseEvent {
    readonly dataTransfer: DataTransfer | null;
}

interface MouseEvent extends UIEvent

interface UIEvent extends Event

image.png

我们可以发现事件对象e也是这样一层一层继承自根接口Event

比较常用的事件类型如下:

  • 鼠标对象类型:MouseEvent
  • 键盘事件类型:KeyboardEvent
  • 普通事件类型:Event

内置工具类型

在使用TypeScript进行开发时,大部分时间我们都是自定义类型,但是TypeScript也有很多内置类型,我们如果对其足够熟悉,那么将可以大大提高我们的开发效率。

Partial

该工具函数可以构造一个新类型,将类型参数 T 中的所有属性变为 可选属性

interface Person {
  name: string
  age: number
}

type PartialType = Partial<Person> // { name ?: string; age ?: number }

Required

该工具函数可以构造一个新类型,将类型参数 T 中的所有属性变为 必选属性

interface Person {
  name: string
  age?: number
}

type RequiredType = Required<Person> // { name: string; age: number }

const student1: RequiredType = {
  name: 'zhangsan',
  age: 18
} 

const student2: RequiredType = { // 错误,缺少 age 属性
  name: 'lisi'
}

cosnt student3: RequiredType = { // 错误,缺少 name 属性
  age: 18
}

Readonly

该工具函数可以构造一个新类型,将类型参数 T 中的所有属性变为 只读属性

interface Person {
  name: string
  age?: number
}
  
type ReadonlyType = Readonly<Person> // { readonly name: string; readonly age: number }
  
const student1: ReadonlyType = {
  name: 'zhangsan',
  age: 18
} 
  
student1.name = 'lisi' // 编译错误,不允许修改;name 属性为只读属性
student1.age = 20 // 编译错误,不允许修改;age 属性为只读属性

Record<K, T>

该工具类型可以构造一个新的对象类型。类型参数 K 提供了对象属性名联合类型,类型参数 T 提供了对象属性值的类型

简单来说,Record<K, T> 可以快速创建一个 “键类型为 K、值类型为 T” 的对象类型,避免手动逐个定义属性。

type K = 'x' | 'y'
type T = number
type R = Record<K, T> // { x: number; y: number }
const a: R = { x: 0, y: 0 } // key 必须为x或者y,value必须是数字类型

Pick<T, K>

该工具类型可以从已有的对象类型中选取指定的属性及其类型,然后构建出一个新的对象类型

类型参数 T 表示源对象的类型,类型参数 K 提供了待选取的属性名类型,它必须是对象类型 T 中存在的属性。

interface Person {
    name: string
    age: number
}

type PickName = Pick<Person, 'name'> // { name: string }
type PickAge = Pick<Person, 'age'> // { age: number }
type PickAll = Pick<Person, 'name' | 'age'> // { name: string; age: number }

type PickError = Pick<Person, 'sex'> // 错误,类型 Person 中不存在 sex 属性

Omit<T, K>

该工具类型的作用与 Pick<T, K> 是互补的,能够从已有的对象属性中剔除指定的属性,然后构建出一个新的对象类型

类型参数 T 表示源对象的类型,类型参数 K 提供了待剔除的属性名的类型,但是它可以是类型参数 T 中不存在的属性。

interface Person {
    name: string
    age: number
}

type OmitName = Omit<Person, 'name'> // { age: number }
type OmitAge = Omit<Person, 'age'> // { name: string }
type OmitAll = Omit<Person, 'name' | 'age'> // {}
type OmitNone = Omit<Person, 'z'> // { name: string; age: number }

Exclude

当想从一个联合类型中去掉一部分类型时,可以用 Exclude 进行构造。它与 Pick 及 Omit 的区别是它剔除联合类型里面的类型,后者是剔除接口里面的类型。

type Exclude<T, U> = T extends U ? never : T;

// test
const n: Exclude<"a" | "b" | "c" | "d" | "e", "a" | "c"> = "b"; 
// const n:"b" | "d" | "e"

Extract

取两个联合类型的交集。

const m: Extract<"a" | "b" | "c" | "d" | "e", "a" | "c"> = "c"; //const m: "a" | "c"

NonNullable

该工具类型能够从类型参数 T 中剔除 null 类型和 undefined 类型,并构建一个新的类型。即获取类型 T 中的所有非空类型。

type NonNull0 = NonNullable<string, null, number> // string, number type NonNull1 = NonNullable<string[], null, undefined> // string[]

Parameters

该工具类型能够获取函数类型 T 的参数类型并使用参数类型构造一个元组类型

type Parameters0 = Parameters<() => string> // []
type Parameters1 = Parameters<(s: string) => void> // [string]

ReturnType

该工具类型能够获取函数类型 T 的返回值类型。

type ReturnType0 = ReturnType<() => string> // string
type ReturnType1 = ReturnType<() => { a: number; b: string}> // { a: number; b: string}