TypeScript进阶
泛型
TIP
- 软件工程中,我们不仅要创建一致的定义良好的API,同时也要考虑可重用性。 组件不仅能够支持当前的数据类型,同时也能支持未来的数据类型,这在创建大型系统时为你提供了十分灵活的功能。
- 在TypeScript中,泛型是一种创建可复用代码组件的工具。这种组件不只能被一种类型使用,而是能被多种类型复用。类似于参数的作用,泛型是一种用以增强类型(types)、接口(interfaces)、函数类型等能力的非常可靠的手段。
泛型函数
掌握:泛型函数基本使用,保证函数内类型复用,且保证类型安全
// 函数的参数是什么类型,返回值就是什么类型
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 或捕获到这个类型,函数任何位置均可使用。
- 泛型函数好处?
- 让函数可以支持不同类型(复用),且保证类型是安全的。
- 调用函数,什么时候可以省略泛型?
- 传入的数据可以推断出你想要的类型,就可以省略。
// 我需要的类型 { name: string, age?: number } 但是推断出来是 { name: string}
let id2 = getId({name:'jack'})
泛型别名
掌握:泛型别名基本使用,实现类型复用
// 对后台返回的数据进行类型定义
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>
是一个变量,可以随意命名,建议遵循大驼峰即可。- 和类型别名配合,在类型别名后加上泛型语法,然后类型别名内就可以使用这个类型参数
- 泛型可以提高类型的
复用性
和灵活性
泛型接口
掌握:泛型接口基本使用,实现类型复用,了解内置泛型接口
// 对象,获取单个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"}
}
- 在接口名称的后面添加
<类型变量>
,那么,这个接口就变成了泛型接口,接口中所有成员都可以使用类型变量。
内置的泛型接口:
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 中的一些常见操作
- 说明:它们都是基于泛型实现的(泛型适用于多种类型,更加通用),并且是内置的,可以直接在代码中使用。 这些工具类型有很多,主要学习以下几个:
Partial<Type>
- 属性转可选
Readonly<Type>
- 属性转只读
Pick<Type, Keys>
- 选取属性
Omit<Type>
- 删除属性
Partial
Partial<T>
用来构造(创建)一个类型,将 Type 的所有属性设置为可选。
type Props = {
id: string
children: number[]
}
type PartialProps = Partial<Props>
- 解释:构造出来的新类型
PartialProps
结构和Props
相同,但所有属性都变为可选的。
Readonly
Readonly<Type>
用来构造一个类型,将 Type 的所有属性都设置为 readonly(只读)。
type Props = {
id: string
children: number[]
}
type ReadonlyProps = Readonly<Props>
- 解释:构造出来的新类型
ReadonlyProps
结构和Props
相同,但所有属性都变为只读的。
let props: ReadonlyProps = { id: '1', children: [] }
// 错误演示
props.id = '2'
- 当我们想重新给 id 属性赋值时,就会报错:无法分配到 "id" ,因为它是只读属性。
Pick
Pick<Type, Keys>
从Type
中选择一组属性来构造新类型。
interface Props {
id: string
title: string
children: number[]
}
type PickProps = Pick<Props, 'id' | 'title'>
- 解释:
- Pick 工具类型有两个类型变量:1 表示选择谁的属性 2 表示选择哪几个属性。 2. 其中第二个类型变量,如果只选择一个则只传入该属性名即可。
- 第二个类型变量传入的属性只能是第一个类型变量中存在的属性。
- 构造出来的新类型 PickProps,只有 id 和 title 两个属性类型。
Omit
Omit<K,T>
类型让我们可以从另一个对象类型中剔除某些属性,并创建一个新的对象类型:
K:是对象类型名称,T:是剔除K类型中的属性名称
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 文件:
既包含类型信息又可执行代码
- 可以被编译为 .js 文件,然后,执行代码
- 用途:编写程序代码的地方
- .d.ts 文件:
只包含类型信息
的类型声明文件- 不会生成 .js 文件,仅用于提供类型信息,在.d.ts文件中不允许出现可执行的代码,只用于提供类型
- 用途:为 JS 提供类型信息
小结:
- .ts 是
implementation
代码实现文件 - .d.ts 是
declaration
类型声明文件 - 如果要为 JS 库或者模块提供类型,就需要类型声明文件
使用-内置类型声明文件
知道:什么是内置的类型什么文件
- 发现,在使用数组时,数组所有方法都会有相应的代码提示以及类型信息:
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
- 查看 forEach 的类型声明,在 VSCode 中会自动跳转到
使用-第三方库类型声明文件
掌握:给第三方库添加对应的类型声明文件
首先,常用的第三方库都有相应的类型声明文件,只是使用的方式不同而已。
第三方库的类型声明文件,有两种存在形式:
- 库自带类型声明文件
- DefinitelyTyped提供
情况1:库本身自带类型声明文件:比如:axios。
解释:导入 axios 后,TS会加载该库的自带类型文件,以提供该库的类型声明。
情况2:由 DefinitelyTyped 提供
DefinitelyTyped 是一个 github 仓库,用来提供高质量 TypeScript 类型声明
比如:jquery,安装后导入,提示:需要安装
@types/jquery
类型声明包当安装
@types/*
类型声明包后,TS 也会自动加载该类声明包,以提供该库的类型声明
小结:
- 第三方库,存在两种方式的类型声明文件:1库自带。 2
DefinitelyTyped
提供。 - 库自带,直接用,无需任何处理。
DefinitelyTyped
提供,下载@types/库名称
自定义类型声明文件
创建自己的类型声明文件:1. 项目内共享类型。2.为已有JS文件提供类型声明。
共享类型(重要)
掌握:使用类型声明文件提供需要共享的TS类型
需求:如果多个 .ts
文件中都用到同一个类型,此时可以创建 .d.ts
文件提供该类型,实现类型共享。
操作步骤:
- 创建
index.d.ts
类型声明文件。 - 创建需要共享的类型,并使用
export
导出(TS 中的类型也可以使用import/export
实现模块化功能)。 - 在需要使用共享类型的
.ts
文件中,通过import
导入即可(.d.ts
后缀导入时,直接省略)。
src/types/data.d.ts
export type Person = {
id: number;
name: string;
age: number;
};
User.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文件中已存在的变量,声明类型,而不是定义一个新的变量。
规则:
- 对于TS 独有的关键字,可以
省略
declare 关键字。如type
interface
等。 - 对于TS、JS都有的关键字,使用
declare
关键字。如let function
等。
add/index.js
const addFn = (a, b) => {
return a + b;
};
const pointFn = (p) => {
console.log('坐标:', p.x, p.y);
};
export { addFn, pointFn }
add/index.d.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
import { addFn , pointFn} from './add';
addFn(3, 10)
pointFn({x: 100, y: 200})
扩展-给第三方模块,提供类型
使用 关键字module
- 定义同名类型声明文件:
包名称.d.ts
- 使用
declare module "包名称" {}
定义模块类型
说明:TS会自动加载:包名称.d.ts
中的类型
代码:
- 定义同名类型声明文件:
jquery.d.ts
declare module "jquery" {
// 注意:module范围内,不需要再使用declare关键字
function $(tagName: "div" | "span"): any
export default $
}
main.ts
中导入jquery
, 观察效果
import $ from 'jquery';
$("span")
注意:
- module范围内,不需要再使用declare关键字
- 类型声明文件名称,module名称,与模块包同名