Skip to content
On this page

TypeScript进阶

泛型

TIP

  • 软件工程中,我们不仅要创建一致的定义良好的API,同时也要考虑可重用性。 组件不仅能够支持当前的数据类型,同时也能支持未来的数据类型,这在创建大型系统时为你提供了十分灵活的功能。
  • 在TypeScript中,泛型是一种创建可复用代码组件的工具。这种组件不只能被一种类型使用,而是能被多种类型复用。类似于参数的作用,泛型是一种用以增强类型(types)、接口(interfaces)、函数类型等能力的非常可靠的手段。

泛型函数

掌握:泛型函数基本使用,保证函数内类型复用,且保证类型安全

ts
// 函数的参数是什么类型,返回值就是什么类型
function getId<T>(id: T): T {
  return id
}

let id1 = getId<number>(1)
let id2 = getId('2')
// TS会进行类型推断,参数的类型作为泛型的类型 getId<string>('2')

小结

  • 泛型函数语法?
    • 函数名称后加上 <T>T是类型参数,是个类型变量,命名建议遵循大驼峰即可。
  • T 什么时候确定?
    • 当你调用函数的时候,传入具体的类型,T 或捕获到这个类型,函数任何位置均可使用。
  • 泛型函数好处?
    • 让函数可以支持不同类型(复用),且保证类型是安全的。
  • 调用函数,什么时候可以省略泛型?
    • 传入的数据可以推断出你想要的类型,就可以省略。
ts
// 我需要的类型 { name: string, age?: number } 但是推断出来是 { name: string}
let id2 = getId({name:'jack'})

泛型别名

掌握:泛型别名基本使用,实现类型复用

ts
// 对后台返回的数据进行类型定义
type User = {
  name: string;
  age: number;
}

type Goods = {
  id: number;
  goodsName: string;
}

type Data<T> = {
  msg: string;
  code: number;
  data: T
}

// 使用类型
const user: Data<User> = {
  code: 200, msg: "响应成功", 
  data: {name: "zs", age: 18}
}
// 使用类型
const goods: Data<Goods> = {
  code: 200, msg: "响应成功", 
  data: {id: 1001, goodsName: "car"}
}

小结:

  • 泛型:定义类型别名后加上<类型参数> 就是泛型语法, 使用的时候传入具体的类型即可
  • <T> 是一个变量,可以随意命名,建议遵循大驼峰即可。
  • 和类型别名配合,在类型别名后加上泛型语法,然后类型别名内就可以使用这个类型参数
  • 泛型可以提高类型的复用性灵活性

泛型接口

掌握:泛型接口基本使用,实现类型复用,了解内置泛型接口

ts
// 对象,获取单个ID函数,获取所有ID函数,ID的类型肯定是一致的,但是可能是数字可能是字符串
interface User  {
  name: string;
  age: number;
}

interface Goods {
  id: number;
  goodsName: string;
}

interface Data<T>  {
  msg: string;
  code: number;
  data: T
}

// 使用类型
const user: Data<User> = {
  code: 200, msg: "响应成功", 
  data: {name: "zs", age: 18}
}
// 使用类型
const goods: Data<Goods> = {
  code: 200, msg: "响应成功", 
  data: {id: 1001, goodsName: "car"}
}
  • 在接口名称的后面添加 <类型变量>,那么,这个接口就变成了泛型接口,接口中所有成员都可以使用类型变量。

内置的泛型接口:

ts
const arr = [1, 2, 3];
// TS有自动类型推断,其实可以看做:const arr: Array<number> = [1, 2, 3]
arr.push(4);
arr.forEach((item) => console.log(item));
  • 可以通过 Ctrl + 鼠标左键(Mac:Command + 鼠标左键) 去查看内置的泛型接口

泛型工具

  • 泛型工具: TS 内置了一些常用的工具类型,来简化 TS 中的一些常见操作
  • 说明:它们都是基于泛型实现的(泛型适用于多种类型,更加通用),并且是内置的,可以直接在代码中使用。 这些工具类型有很多,主要学习以下几个:
  1. Partial<Type>
    1. 属性转可选
  2. Readonly<Type>
    1. 属性转只读
  3. Pick<Type, Keys>
    1. 选取属性
  4. Omit<Type>
    1. 删除属性

Partial

  • Partial<T> 用来构造(创建)一个类型,将 Type 的所有属性设置为可选。
ts
type Props =  {
  id: string
  children: number[]
}

type PartialProps = Partial<Props>
  • 解释:构造出来的新类型 PartialProps 结构和 Props 相同,但所有属性都变为可选的。

Readonly

  • Readonly<Type> 用来构造一个类型,将 Type 的所有属性都设置为 readonly(只读)。
ts
type Props =  {
  id: string
  children: number[]
}

type ReadonlyProps = Readonly<Props>
  • 解释:构造出来的新类型 ReadonlyProps 结构和 Props 相同,但所有属性都变为只读的。
ts
let props: ReadonlyProps = { id: '1', children: [] }
// 错误演示
props.id = '2'
  • 当我们想重新给 id 属性赋值时,就会报错:无法分配到 "id" ,因为它是只读属性。

Pick

  • Pick<Type, Keys>Type 中选择一组属性来构造新类型。
ts
interface Props {
  id: string
  title: string
  children: number[]
}
type PickProps = Pick<Props, 'id' | 'title'>
  • 解释:
    1. Pick 工具类型有两个类型变量:1 表示选择谁的属性 2 表示选择哪几个属性。 2. 其中第二个类型变量,如果只选择一个则只传入该属性名即可。
    2. 第二个类型变量传入的属性只能是第一个类型变量中存在的属性。
    3. 构造出来的新类型 PickProps,只有 id 和 title 两个属性类型。

Omit

Omit<K,T> 类型让我们可以从另一个对象类型中剔除某些属性,并创建一个新的对象类型:

K:是对象类型名称,T:是剔除K类型中的属性名称

ts
type Props {
	name: string
	age: number
	hobby: string
	sex: “男" | "女"
}

// 如果我不希望有hobby和sex这两个属性,可以这么写
type NewProps = Omit<Props, "hobby" | "sex">
// 等价于
type NewProps  {
  name: string
  age: number
}

TypeScript类型声明文件

TIP

typescript 类型声明文件相关知识

基本介绍

知道:TS类型声明文件是什么以及作用

项目中安装的第三方库里面都是打包后的JS代码,但是我们使用的时候却有对应的TS类型提示,这是为什么呢?

  • 在第三方库中的JS代码都有对应的 TS类型声明文件

什么是类型什么文件?

  • 通俗地来讲,在 TypeScript 中以 .d.ts 为后缀的文件,我们称之为 TypeScript 类型声明文件。它的主要作用是描述 JavaScript 模块内所有导出成员的类型信息。

TS中的两种文件类型

TS 中有两种文件类型:1.ts 文件 2.d.ts

  • .ts 文件:
    1. 既包含类型信息又可执行代码
    2. 可以被编译为 .js 文件,然后,执行代码
    3. 用途:编写程序代码的地方
  • .d.ts 文件:
    1. 只包含类型信息的类型声明文件
    2. 不会生成 .js 文件,仅用于提供类型信息,在.d.ts文件中不允许出现可执行的代码,只用于提供类型
    3. 用途:为 JS 提供类型信息

小结:

  • .ts 是 implementation 代码实现文件
  • .d.ts 是 declaration 类型声明文件
  • 如果要为 JS 库或者模块提供类型,就需要类型声明文件

使用-内置类型声明文件

知道:什么是内置的类型什么文件

  • 发现,在使用数组时,数组所有方法都会有相应的代码提示以及类型信息:
ts
const strs = ['a', 'b', 'c']
// 鼠标放在 forEach 上查看类型
strs.forEach

内置类型声明文件 : TS为 JS 运行时可用的所有标准化内置 API 都提供了声明文件

  • 可以通过 Ctrl + 鼠标左键(Mac:Command + 鼠标左键)来查看内置类型声明文件内容
    • 查看 forEach 的类型声明,在 VSCode 中会自动跳转到 lib.es5.d.ts 类型声明文件中
    • 像 window、document 等 BOM、DOM API 也都有相应的类型声明文件 lib.dom.d.ts

使用-第三方库类型声明文件

掌握:给第三方库添加对应的类型声明文件

首先,常用的第三方库都有相应的类型声明文件,只是使用的方式不同而已。

第三方库的类型声明文件,有两种存在形式:

  1. 库自带类型声明文件
  2. DefinitelyTyped提供

情况1:库本身自带类型声明文件:比如:axios。

image-20230204135208002

解释:导入 axios 后,TS会加载该库的自带类型文件,以提供该库的类型声明。


情况2:由 DefinitelyTyped 提供

  • DefinitelyTyped 是一个 github 仓库,用来提供高质量 TypeScript 类型声明

  • 比如:jquery,安装后导入,提示:需要安装 @types/jquery 类型声明包

  • 当安装 @types/* 类型声明包后,TS 也会自动加载该类声明包,以提供该库的类型声明

image-20230204142018977

image-20230204141823746

小结:

  1. 第三方库,存在两种方式的类型声明文件:1库自带。 2DefinitelyTyped提供。
  2. 库自带,直接用,无需任何处理。
  3. DefinitelyTyped提供,下载 @types/库名称

自定义类型声明文件

创建自己的类型声明文件:1. 项目内共享类型。2.为已有JS文件提供类型声明。

共享类型(重要)

掌握:使用类型声明文件提供需要共享的TS类型

需求:如果多个 .ts 文件中都用到同一个类型,此时可以创建 .d.ts 文件提供该类型,实现类型共享。

操作步骤:

  1. 创建 index.d.ts 类型声明文件。
  2. 创建需要共享的类型,并使用 export 导出(TS 中的类型也可以使用 import/export 实现模块化功能)。
  3. 在需要使用共享类型的 .ts 文件中,通过 import 导入即可(.d.ts 后缀导入时,直接省略)。

src/types/data.d.ts

ts
export type Person = {
  id: number;
  name: string;
  age: number;
};

User.ts

ts
import { Person } from './types/data'
const p: Person = {
  id: 100,
  name: 'jack',
  age: 19
}

给JS文件提供类型

了解:使用类型声明文件给JS文件添加类型

说明:

  • .ts文件中,也可以使用.js文件。

  • .ts文件中导入 .js 文件时,TS 会自动加载与 .js 同名的 .d.ts 文件,以提供类型声明。


语法:declare 关键字

解释:为js文件中已存在的变量,声明类型,而不是定义一个新的变量。

规则:

  1. 对于TS 独有的关键字,可以省略 declare 关键字。如 type interface 等。
  2. 对于TS、JS都有的关键字,使用 declare 关键字。如let function等。

add/index.js

js
const addFn = (a, b) => {
  return a + b;
};

const pointFn = (p) => {
  console.log('坐标:', p.x, p.y);
};

export { addFn, pointFn }

add/index.d.ts

ts
declare const addFn: (a: number, b: number) => number;

type Position = {
  x: number;
  y: number;
};

declare const pointFn: (p: Position) => void;
// 💥 注意要导出
export { addFn , pointFn};

main.ts

ts
import { addFn , pointFn} from './add';

addFn(3, 10)

pointFn({x: 100, y: 200})

扩展-给第三方模块,提供类型

使用 关键字module

  1. 定义同名类型声明文件:包名称.d.ts
  2. 使用 declare module "包名称" {} 定义模块类型

说明:TS会自动加载:包名称.d.ts中的类型

代码:

  1. 定义同名类型声明文件:jquery.d.ts
ts
declare module "jquery" {
  // 注意:module范围内,不需要再使用declare关键字
  function $(tagName: "div" | "span"): any
  export default $
}
  1. main.ts中导入jquery, 观察效果
ts
import $ from 'jquery';

$("span")

注意:

  1. module范围内,不需要再使用declare关键字
  2. 类型声明文件名称module名称,与模块包同名

Released under the MIT License.