当前位置:   article > 正文

【TypeScript】TS全解

【TypeScript】TS全解

一、TypeScript 介绍

(一)TypeScript

  • 背景:JS 属于弱类型语言,易产生类型错误 ,项目开发时增加了查改 Bug 的时间,影响效率
  • 定义:TypeScript 简称 TS,是 JavaScript 的超集,包含所有 JS 功能且延伸类型支持功能
  • 区别
    • JavaScript:动态类型,执行期做类型检查,代码执行时发现错误
    • TypeScript:静态类型,编译期做类型检查,代码执行前发现错误;配合 VSCode,代码编写时发现错误

(二)优势

  • 程序中随时有代码提示,减少查改 Bug 时间,提升开发效率
  • 强大的类型系统提升了代码的可维护性,使得重构代码更加容易
  • 支持最新的 ECMAScript 语法,优先体验最新语法
  • TS 类型推断机制,不需要在代码中的每个地方都显示标注类型

二、TypeScript 使用准备

(一)安装编译 TS 的工具包

  • 背景:Node.js / 浏览器,仅识别 JS 代码,需要先将 TS 转为 JS 代码才能运行
  • 安装命令:npm i -g typescript
    • TS 包:编译 TS 代码的包,提供 tsc 命令实现 TS => JS
    • 查看版本:tsc –v

(二)编译并运行 TS 代码

  • 创建 TS:新建 .ts 文件
  • 编译 TS:在终端中输入命令 tsc file.ts
  • 执行 JS:在终端中输入命令 node file.js
    • 所有合法 JS 代码都是 TS 代码,JS 基础上只学习 TS 类型即可
    • 由 TS 编译生成同级 JS 文件,代码中不含类型信息

(三)简化运行 TS 的步骤

  • 简化方式:使用 ts-node 包,在 Node.js 中执行 TS 代码
  • 安装命令:npm i -g ts-node
  • 使用方式:ts-node file.ts
    • ts-node 不生成 js 文件

三、TypeScript 常用类型

  • 类型系统:TS 是 JS 的超集,提供 JS 所有功能并额外增加类型系统
    • JS 代码都是 TS 代码
    • JS 类型不检查变化,TS 检查变量类型变化
  • 主要优势:可以显示标记出代码中的意外行为,从而降低了发生错误的可能性

(一)类型注解

1、类型注解

  • 体现:: number
  • 作用:为变量添加类型约束
let age: number = 18
  • 1

2、类型别名 type

  • 特点:支持所有类型创建类型别名,一般用于复杂数据类型
// 创建类型别名
type Person = {
  name: string
  sayHi(): void
  greet(name: string): void // 普通函数带参数的方法类型
  meet: (name: string) => void // 箭头函数带参数的方法类型
}

// 使用类型别名作为对象的类型
let person: Person = {
  name: 'jack',
  sayHi() {}
  greet(name) {
    console.log(name)
  }
  meet(name) {
      console.log(name)
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

(二)常用基础类型

1、JS 已有类型

(1)原始类型
  • 特点:简单类型,按 JS 类型名称书写
  • 包含:number / string / boolean / null / undefined / symbol
let age: number = 18
let myName: string = '老师'
let isLoading: boolean = false
let nu: null = null
let un: undefined = undefined
let smb: symbol = symbol
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
(2)数组类型
  • 写法一:arrType[](推荐)
  • 写法二:Array<arrType>
// 写法一:
let numbers: number[] = [1, 3, 5]
// 写法二:
let strings: Array<string> = ['a', 'b', 'c']
  • 1
  • 2
  • 3
  • 4
(3)对象类型
  • JS 中的对象由属性和方法构成,而 TS 对象类型就是描述对象结构
    • 使用 {} 描述对象结构
    • 属性采用 key:type 形式
    • 方法采用 fn():type 形式
// 空对象
let person: {} = {}

// 有属性的对象
let person: { name: string } = {
  name: '同学'
}

// 既有属性又有方法的对象
let person: {
  name: string
  sayHi(): void
} = {
  name: 'jack',
  sayHi() {}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 对象可选属性
    • 定义:对象的属性或方法,可以为可选
    • 语法:使用 ? 表示
type Config = {
  url: string
  method?: string
}

function myAxios(config: Config) {
  console.log(config)
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
(4)函数类型
  • 特点:约束函数参数返回值的类型
  • 单独指定
// 函数声明
function add(num1: number, num2: number): number {
  return num1 + num2
}

// 箭头函数
const add = (num1: number, num2: number): number => {
  return num1 + num2
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 同时指定
    • 特点:函数表达式可以通过类箭头函数的语法为函数添加类型,声明函数不支持
type AddFn = (num1: number, num2: number) => number // 使用 type

const add: AddFn = (num1, num2) => {
  return num1 + num2
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 函数可选参数
    • 特点:参数不固定情况下,函数参数指定类型为可选参数
    • 语法:在不固定参数名称后面添加 ?
    • 注意:可选参数只能出现在参数列表的最后,也就是说可选参数后面不能再出现必选参数
function mySlice(start?: number, end?: number): void {
  console.log('起始索引:', start, '结束索引:', end)
}
  • 1
  • 2
  • 3

2、TS 新增类型

(1)联合类型
  • 定义:由两个或多个其他类型组成的类型,表示可以是这些类型中的任意一种
  • 语法:使用 | 联合
let arr: (number | string)[] = [1, 'a', 3, 'b']
  • 1
(2)自定义类型/类型别名
  • 定义:为任意类型起别名
  • 语法:使用 type 声明别名,别名推荐使用大写字母开头
  • 场景:当同一类型(复杂)被多次使用时,可以通过类型别名,简化该类型的使用
type CustomArray = (number | string)[]

let arr1: CustomArray = [1, 'a', 3, 'b']
let arr2: CustomArray = ['x', 'y', 6, 7]
  • 1
  • 2
  • 3
  • 4
(3)接口
  • 定义:为对象类型添加接口
  • 语法:使用 interface 声明,接口名称推荐以 I 开头
  • 场景:当对象类型被多次使用,可用接口描述对象类型,达到复用目的
interface IPerson {
  name: string
  age: number
  sayHi(): void
}

let person: IPerson = {
  name: 'jack',
  age: 19,
  sayHi() {}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • interface vs type
    • 相同:可以给对象指定类型
    • 不同
      • 接口:仅为对象指定类型,有继承性
      • 类型别名:不仅为对象指定类型,支持任意类型指定类型
// 为对象类型提供类型接口
interface IPerson {
  name: string
  age: number
  sayHi(): void
}

// 为对象类型创建类型别名
type IPerson = {
  name: string
  age: number
  sayHi(): void
}

// 为联合类型创建类型别名
type NumStr = number | string
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 接口继承
    • 语法:使用 extends 继承
interface Point2D { x: number; y: number }
// 继承 Point2D
interface Point3D extends Point2D {
  z: number
}
  • 1
  • 2
  • 3
  • 4
  • 5
(4)元组
  • 特点:确定数组中元素类型元素个数
  • 语法:元组 Tuple
// 普通数组,不确定元素个数
let position: number[] = [116.2317, 39.5427]

// 元祖,确定元素类型和个数
let position: [number, number] = [39.5427, 116.2317]
  • 1
  • 2
  • 3
  • 4
  • 5
(5)字面量类型
  • 定义:任意 JS 字面量(如对象、数字等)可以作为 TS 中的类型
let str1 = 'Hello TS' // let 定义 str1 是变量
let str1: string = 'Hello TS'

const str2 = 'Hello TS' // const 定义 str2 是常量
const str2: 'Hello TS' = 'Hello TS'
  • 1
  • 2
  • 3
  • 4
  • 5
  • 使用模式和场景
    • 特点:配合联合类型使用,表示一组明确的可选值列表
    • 优势:相比 string 类型,字面量类型更精确、严谨
// 比如在贪吃蛇游戏中,游戏的方向可选值只能是上、下、左、右
// 使用自定义类型:
type Direction = 'up' | 'down' | 'left' | 'right'

function changeDirection(direction: Direction) {
  console.log(direction)
}

// 调用函数时,会有类型提示:
changeDirection('up')
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
(6)枚举(了解)
  • 特点:类似字面量类型 + 联合类型组合,也可以表示一组明确的可选值,还支持直接使用枚举名称作为类型注解
  • 语法:使用 enum 关键字定义枚举
    • 约定枚举名称以大写字母开头
    • 支持使用枚举名称作为类型注解
// 创建枚举
enum Direction { Up, Down, Left, Right }

// 使用枚举类型
function changeDirection(direction: Direction) {
  console.log(direction)
}

// 调用函数时,通过点(.)语法访问枚举 Direction 成员
changeDirection(Direction.Up)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 数字枚举
    • 特点:枚举成员的值为数字,默认从 0 开始自增,也可以给枚举成员初始化值
// Down -> 11、Left -> 12、Right -> 13
enum Direction { Up = 10, Down, Left, Right }

enum Direction { Up = 2, Down = 4, Left = 8, Right = 16 }
  • 1
  • 2
  • 3
  • 4
  • 字符串枚举
    • 特点:枚举成员的值为字符串,由于没有自增行为,每个成员必须有初始值
enum Direction {
  Up = 'UP',
  Down = 'DOWN',
  Left = 'LEFT',
  Right = 'RIGHT'
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 枚举实现原理
    • 其他类型仅为类型,而枚举不仅为类型,还提供值(枚举成员均有值),即其他类型编译为 JS 代码时自动移除,但枚举类型会被编译为 JS 代码
enum Direction {
  Up = 'UP',
  Down = 'DOWN',
  Left = 'LEFT',
  Right = 'RIGHT'
}

// 编译为以下 JS 代码
var Direction;

(function (Direction) {
  Direction['Up'] = 'UP'
  Direction['Down'] = 'DOWN'
  Direction['Left'] = 'LEFT'
  Direction['Right'] = 'RIGHT'
})(Direction || Direction = {})
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
(7)void 类型
  • 特点:如果函数无返回值,则使用 void
function greet(name: string): void {
  console.log('Hello', name)
}

// 如果什么都不写,此时,add 函数的返回值类型为: void
const add = () => {}
// 这种写法是明确指定函数返回值类型为 void,与上面不指定返回值类型相同
const add = (): void => {}

// 如果指定返回值类型为 undefined,此时,函数体中必须显示的 return undefined 才可以
const add = (): undefined => {
  // 此处,返回的 undefined 是 JS 中的一个值
  return undefined
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
(8)any 类型(不推荐)
  • 特点:值类型为 any 时,可以对该值进行任意操作且没有代码提示,不推荐使用
let obj: any = { x: 0 }

obj.bar = 100
obj()
const n: number = obj
  • 1
  • 2
  • 3
  • 4
  • 5
  • 使用场景
    • 临时使用 any 避免书写很长、很复杂的类型
    • 其他隐式具有 any 类型的情况

(三)TS 类型方法

1、类型推论

  • 定义:TS 中,某些没有指出类型的地方,TS 的类型推论机制会帮助提供类型
  • 场景
    • 声明变量并初始化时
    • 决定函数返回值时
// 变量 age 的类型被自动推断为:number
let age = 18

// 函数返回值的类型被自动推断为:number
function add(num1: number, num2: number) {
  return num1 + num2
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 使用
    • 类型注解能省就省,充分利用TS类型推论的能力,提升开发效率
    • 可以通过鼠标放在变量名称上,利用 VSCode 提示查看类型
    • 在 VSCode 中写代码,多看方法、属性类型

2、类型断言

  • 特点:开发者比 TS 更明确一个值的类型,使用类型断言指定更具体类型
  • 语法:使用 as 关键字实现类型断言,后面补充一个更具体的类型
    • 技巧:在浏览器控制台,通过 __proto__ 获取 DOM 元素的类型
// 返回类型为 HTMLElement,只包含所有标签公共的属性或方法,不包含 a 标签特有的 href 等属性
const aLink = document.getElementById('link')

// 使用类型断言指定更加具体的类型,HTMLAnchorElement 是 HTMLElement 的子类型
const aLink = document.getElementById('link') as HTMLAnchorElement
  • 1
  • 2
  • 3
  • 4
  • 5
  • 语法:使用 <> 语法(不推荐)
// 在react的jsx中使用会报错
const aLink = <HTMLAnchorElement>document.getElementById('link')
  • 1
  • 2

3、typeof

  • 特点:TS 提供 typeof 操作符在类型上下文中引用变量或属性类型(类型查询)
  • 场景:根据已有变量的值获取类型,简化类型书写
let p = { x: 1, y: 2 }

// 简化前
function formatPoint(point: { x: number; y: number }) {}

// 简化后
function formatPoint(point: typeof p) {}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

四、TypeScript 高级类型

(一)泛型

  • 定义:在保证类型安全前提下,让函数等与多种类型一起工作,从而实现复用,常用于函数、接口、class 中
  • 需求:创建一个 id 函数,传入什么数据就返回该数据本身(也就是说,参数和返回值类型相同)

1、创建泛型函数

  • 语法:在函数名称后面添加 <类型变量 Type>,即一种特殊类型的变量,相当于类型容器
function id<Type>(value: Type): Type { return value }

function id<T>(value: T): T { return value }
  • 1
  • 2
  • 3

2、调用泛型函数

  • 语法:在函数名称后面添加 <指定类型>
const num = id<number>(10)
const str = id<string>('a')

// 简化泛型函数调用,省略 <指定类型>,TS 进行类型参数推断
let num = id(10)
let str = id('a')
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

3、泛型约束

  • 背景:泛型函数的类型变量 Type 可以代表任意类型,导致无法访问具体属性,需要为泛型添加约束收缩类型
(1)指定更加具体的类型
  • 方法:将类型修改为相关具体类型
function id<Type>(value: Type[]): Type[] {
  console.log(value.length)
  return value
}
  • 1
  • 2
  • 3
  • 4
(2)添加约束
  • 创建接口:创建描述约束的接口 Ixxx,要求接口提供相关属性
  • 添加约束:通过 extends 关键字使用该接口,为泛型添加约束和属性
// 创建一个接口
interface ILength { length: number }

// Type extends ILength 添加泛型约束
// 传入类型必须满足 ILength 接口要求,即 number 类型的 length 属性
function id<Type extends ILength>(value: Type): Type {
  console.log(value.length)
  return value
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

4、keyof

  • 定义:泛型的类型变量可以有多个,并且类型变量之间可以约束
  • 语法:keyof 关键字接收一个类型变量,生成其键名称的联合类型
    • 类型变量 Key 受 Type 约束,只是 Type 所有键之一,只能访问对象中存在的属性
function getProp<Type, Key extends keyof Type>(obj: Type, key: Key) {
// keyof Type 获取 person 对象所有键的联合类型,即`'name' | 'age'`
  return obj[key]
}
let person = { name: 'jack', age: 18 }
getProp(person, 'name')

// Type extends object:Type 应该是一个对象类型
// 对象类型,应该用 object ,而不是 Object
function getProperty<Type extends object, Key extends keyof Type>(obj: Type, key: Key) {
  return obj[key]
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

5、泛型接口

  • 定义:接口可以配合泛型使用,增加灵活性和复用性
  • 语法
    • 定义:在接口名称后面添加 <类型变量>
    • 使用:需要显式指定具体的类型 Ixxx<类型>
interface IdFunc<Type> {
  id: (value: Type) => Type
  ids: () => Type[]
}

// 接口中所有成员都可以使用类型变量
let obj: IdFunc<number> = {
  id(value) { return value }, // 参数和返回值类型是 number
  ids() { return [1, 3, 5] }  // 返回值类型是 number[]
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • JS 中的泛型接口
    • JS 数组在 TS 中就是一个泛型接口
    • 使用数组时,TS 会根据数组的不同类型,自动将类型变量设置为相应类型
    • 通过 Ctrl + 鼠标左键(Mac:Command + 鼠标左键)来查看具体的类型信息
const strs = ['a', 'b', 'c']
// 鼠标放在 forEach 上查看类型
strs.forEach

const nums = [1, 3, 5]
// 鼠标放在 forEach 上查看类型
nums.forEach
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

6、泛型工具类型

  • 定义:TS 内置一些常用的工具类型,简化 TS 中常见操作
(1)Partial
  • 定义:构造一个类型,将 Type 的所有属性设置为可选
  • 语法:Partial<Type>
type Props =  {
  id: string
  children: number[]
}

// 构造出的新类型 PartialProps 结构和 Props 相同,但所有属性都为可选
type PartialProps = Partial<Props>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
(2)Readonly
  • 定义:构造一个类型,将 Type 的所有属性都设置为只读
  • 语法:Readonly<Type>
type Props =  {
  id: string
  children: number[]
}

// 构造出的新类型 PartialProps 结构和 Props 相同,但所有属性都为只读
type ReadonlyProps = Readonly<Props>

// 错误演示
let props: ReadonlyProps = { id: '1', children: [] }
props.id = '2'
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
(3)Pick
  • 定义:从 Type 中选择一组属性构造新类型
  • 语法:Pick<Type, Keys>
interface Props {
  id: string
  title: string
  children: number[]
}

// 第二个类型变量传入的属性只能是第一个类型变量中存在的属性
// 构造出来的新类型 PickProps,只有 id 和 title 两个属性类型
type PickProps = Pick<Props, 'id' | 'title'>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
(4)Omit
  • 定义:从 Type 中剔除一组属性构造新类型
  • 语法:Omit<K,T>
interface Props {
  id: string
  title: string
  children: number[]
}

// 第二个类型变量传入的属性只能是第一个类型变量中存在的属性
// 构造出来的新类型 OmitProps,只有 children 属性类型
type OmitProps = Omit<Props, 'id' | 'title'>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

(二)索引签名类型

  • 多数情况下,在使用对象前就确定对象的结构,并为对象添加准确的类型
  • 当无法确定对象中有哪些属性(或者说对象中可以出现任意多个属性),用到索引签名类型
interface AnyObject {
  [key: string]: number
}
let obj: AnyObject = {
  a: 1,
  b: 2,
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 解释:
    1. 使用 [key: string] 来约束该接口中允许出现的属性名称。表示只要是 string 类型的属性名称,都可以出现在对象中。
    2. 这样,对象 obj 中就可以出现任意多个属性(比如,a、b 等)。
    3. key 只是一个占位符,可以换成任意合法的变量名称。
    4. 隐藏的前置知识:JS 中对象({})的键是 string 类型的

数组索引类型签名

  • 在 JS 中数组是一类特殊的对象,特殊在数组的键(索引)是数值类型
  • 并且,数组也可以出现任意多个元素。所以,在数组对应的泛型接口中,也用到了索引签名类型。
interface MyArray<T> {
  [n: number]: T
}
let arr: MyArray<number> = [1, 3, 5]
  • 1
  • 2
  • 3
  • 4
  • 解释:
    1. MyArray 接口模拟原生的数组接口,并使用 [n: number] 来作为索引签名类型。
    2. 该索引签名类型表示:只要是 number 类型的键(索引)都可以出现在数组中,或者说数组中可以有任意多个元素。
    3. 同时也符合数组索引是 number 类型这一前提。

(三)映射类型

  • 映射类型:基于旧类型创建新类型(对象类型),减少重复、提升开发效率。
    比如,类型 PropKeys 有 x/y/z,另一个类型 Type1 中也有 x/y/z,并且 Type1 中 x/y/z 的类型相同:
type PropKeys = 'x' | 'y' | 'z'
type Type1 = { x: number; y: number; z: number }
  • 1
  • 2
  • 这样书写没错,但 x/y/z 重复书写了两次。像这种情况,就可以使用映射类型来进行简化。
type PropKeys = 'x' | 'y' | 'z'
type Type2 = { [Key in PropKeys]: number }
  • 1
  • 2
  • 解释:
    1. 映射类型是基于索引签名类型的,所以,该语法类似于索引签名类型,也使用了 []。
    2. Key in PropKeys 表示 Key 可以是 PropKeys 联合类型中的任意一个,类似于 forin(let k in obj)。
    3. 使用映射类型创建的新对象类型 Type2 和类型 Type1 结构完全相同。
    4. 注意:映射类型只能在类型别名中使用,不能在接口中使用

根据对象创建

映射类型除了根据联合类型创建新类型外,还可以根据对象类型来创建:

type Props = { a: number; b: string; c: boolean }
type Type3 = { [key in keyof Props]: number }
  • 1
  • 2
  • 解释:
    1. 首先,先执行 keyof Props 获取到对象类型 Props 中所有键的联合类型即,‘a’ | ‘b’ | ‘c’。
    2. 然后,Key in ... 就表示 Key 可以是 Props 中所有的键名称中的任意一个。

内置映射类型实现分析

  • 实际上,前面讲到的泛型工具类型(比如,Partial)都是基于映射类型实现的。
  • 比如,Partial 的实现:
type Partial<T> = {
  [P in keyof T]?: T[P]
}

type Props = { a: number; b: string; c: boolean }
type PartialProps = Partial<Props>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 解释:
    1. keyof T 即 keyof Props 表示获取 Props 的所有键,也就是:‘a’ | ‘b’ | ‘c’。
    2. 在 [] 后面添加 ?(问号),表示将这些属性变为可选的,以此来实现 Partial 的功能。
    3. 冒号后面的 T[P] 表示获取 T 中每个键对应的类型。比如,如果是 ‘a’ 则类型是 number;如果是 ‘b’ 则类型是 string。
    4. 最终,新类型 PartialProps 和旧类型 Props 结构完全相同,只是让所有类型都变为可选了。

索引访问类型

  • 刚刚用到的 T[P] 语法,在 TS 中叫做索引访问类型
  • 作用:用来查询属性的类型
type Props = { a: number; b: string; c: boolean }
type TypeA = Props['a']
  • 1
  • 2
  • 解释:Props['a'] 表示查询类型 Props 中属性 ‘a’ 对应的类型 number。所以,TypeA 的类型为 number
  • 注意:[] 中的属性必须存在于被查询类型中,否则就会报错。

同时查询多个索引的类型

  • 索引查询类型的其他使用方式:同时查询多个索引的类型
type Props = { a: number; b: string; c: boolean }

type TypeA = Props['a' | 'b'] // string | number
  • 1
  • 2
  • 3
  • 解释:使用字符串字面量的联合类型,获取属性 a 和 b 对应的类型,结果为: string | number。
type TypeA = Props[keyof Props] // string | number | boolean
  • 1
  • 解释:使用 keyof 操作符获取 Props 中所有键对应的类型,结果为: string | number | boolean。

五、在原有项目使用 TS

创建新项目

  • 命令:npx create-react-app my-app --template typescript

  • 说明:在命令行中,添加 --template typescript 表示创建支持 TS 的项目

  • 项目目录的变化:

    1. 在项目根目录中多了一个文件:tsconfig.json
      • TS 的配置文件
    2. 在 src 目录中,文件的后缀有变化,由原来的 .js 变为 .ts.tsx
      • .ts ts 文件的后缀名
      • .tsx 是在 TS 中使用 React 组件时,需要使用该后缀
    3. 在 src 目录中,多了 react-app-env.d.ts 文件
      • .d.ts 类型声明文件,用来指定类型

tsconfig 文档介绍

  • tsconfig 文档介绍
  • tsconfig.json 是 TS 项目的配置文件,通过 tsc --init 生成
  • 所有的配置项都可以通过鼠标移入的方式,查看配置项解释说明
{
  // 编译选项
  "compilerOptions": {
    // 生成代码的语言版本:将我们写的 TS 代码编译成哪个版本的 JS 代码
    // 命令行: tsc --target es5 11-测试TS配置文件.ts
    "target": "es5",
    // 指定要包含在编译中的 library
    "lib": ["dom", "dom.iterable", "esnext"],
    // 允许 ts 编译器编译 js 文件
    "allowJs": true,
    // 跳过类型声明文件的类型检查
    "skipLibCheck": true,
    // es 模块 互操作,屏蔽 ESModule 和 CommonJS 之间的差异
    "esModuleInterop": true,
    // 允许通过 import x from 'y' 即使模块没有显式指定 default 导出
    "allowSyntheticDefaultImports": true,
    // 开启严格模式
    "strict": true,
    // 对文件名称强制区分大小写
    "forceConsistentCasingInFileNames": true,
    // 为 switch 语句启用错误报告
    "noFallthroughCasesInSwitch": true,
    // 生成代码的模块化标准
    "module": "esnext",
    // 模块解析(查找)策略
    "moduleResolution": "node",
    // 允许导入扩展名为.json的模块
    "resolveJsonModule": true,
    // 是否将没有 import/export 的文件视为旧(全局而非模块化)脚本文件
    "isolatedModules": true,
    // 编译时不生成任何文件(只进行类型检查)
    "noEmit": true,
    // 指定将 JSX 编译成什么形式
    "jsx": "react-jsx"
  },
  // 指定允许 ts 处理的目录
  "include": ["src"]
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38

(一)typescript 声明文件

  • 背景:几乎所有 JS 应用都会引入第三方库完成任务需求,第三方库不管是否用 TS 编写,都有相应的 TS 类型
  • 定义:typescript 声明文件为已存在的 JS 库提供类型信息,有代码提示、类型保护等机制
  1. TS 的两种文件类型
  2. 类型声明文件的使用说明

1、TS 两种文件类型

  • .ts 文件
    • 既包含类型信息又可执行代码
    • 可以被编译为 .js 文件,然后执行代码
    • 用途:编写程序代码的地方
  • .d.ts 文件
    • 只包含类型信息的类型声明文件
    • 不会生成 .js 文件,不允许出现可执行的代码,仅用于提供类型信息
    • 用途:为 JS 提供类型信息
  • 总结:.ts 是 implementation(代码实现文件);.d.ts 是 declaration(类型声明文件)

2、类型声明文件使用说明

(1)内置类型声明文件
    • TS 为 JS 运行时可用的所有标准化内置 API 都提供了声明文件
const strs = ['a', 'b', 'c']
// 鼠标放在 forEach 上查看类型
strs.forEach
  • 1
  • 2
  • 3
  • 实际上这都是 TS 提供的内置类型声明文件
  • 可以通过 Ctrl + 鼠标左键(Mac:Command + 鼠标左键)来查看内置类型声明文件内容
  • 比如,查看 forEach 方法的类型声明,在 VSCode 中会自动跳转到 lib.es5.d.ts 类型声明文件中
  • 当然,像 window、document 等 BOM、DOM API 也都有相应的类型声明(lib.dom.d.ts)
(2)第三方库的类型声明文件
  • 常用的第三方库都有相应的类型声明文件
    • 库自带类型声明文件,如axios,查看 node_modules/axios 目录
    • DefinitelyTyped 提供,一个提供高质量 TS 类型声明的 github 仓库
    • 实际项目开发时,第三方库没有自带的声明文件,VSCode 会给出明确的提
    • TS 官方文档提供了一个页面,可以来查询 @types/* 库
(3)创建自己的类型声明文件
  • 项目内共享类型
    • 多个 .ts 文件中都用到同一个类型,此时可以创建 .d.ts 文件提供该类型,实现类型共享
    • 操作步骤
      • 创建 index.d.ts 类型声明文件。
      • 创建需要共享的类型,并使用 export 导出
      • 在需要使用共享类型的 .ts 文件中,通过 import 导入
  • 为已有 JS 文件提供类型声明
    • 背景
      • 将 JS 项目迁移到 TS 项目时,为了让已有的 .js 文件有类型声明
      • 成为库作者,创建库给其他人使用
(4)类型声明文件的使用说明
  • 说明:TS 项目中可以使用 .js 文件,导入 .js 文件时,TS 会自动加载与 .js 同名的 .d.ts 文件,以提供类型声明
  • declare:用于类型声明,为其他地方(如 .js 文件)已存在的变量声明类型,而不是创建一个新的变量
    • type、interface 这些明确 TS 类型可以省略 declare 关键字
    • let、function 等具有双重含义(在 JS、TS 中都能用),必须用 declare 关键字,明确指定此处用于类型声明。
let count = 10
let songName = '痴心绝对'
let position = {
  x: 0,
  y: 0
}

function add(x, y) {
  return x + y
}

function changeDirection(direction) {
  console.log(direction)
}

const fomartPoint = point => {
  console.log('当前坐标:', point)
}

export { count, songName, position, add, changeDirection, fomartPoint }

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

定义类型声明文件

declare let count:number

declare let songName: string

interface Position {
  x: number,
  y: number
}

declare let position: Position

declare function add (x :number, y: number) : number

type Direction = 'left' | 'right' | 'top' | 'bottom'

declare function changeDirection (direction: Direction): void

type FomartPoint = (point: Position) => void

declare const fomartPoint: FomartPoint

export {
  count, songName, position, add, changeDirection, FomartPoint, fomartPoint
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

(二)在现有项目中添加 TS

  • CRA 添加 ts 文档
  • 步骤
    • 安装包:yarn add typescript @types/node @types/react @types/react-dom @types/jest
    • jsconfig.json改成 path.tsconfig.json
    • 将原来通过 React 脚手架创建的 TS 项目中的 tsconfig.json 中的配置,拷贝到咱们自己的项目中
    • 创建 path.tsconfig.json 文件,将原来 jsconfig.json 文件中的内容拿过来
{
  "compilerOptions": {
    "baseUrl": "./",
    "paths": {
      "@/*": ["src/*"],
      "@scss/*": ["src/assets/styles/*"]
    }
  }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • tsconfig.json 中,添加配置
{
  // 添加这一句
  "extends": "./path.tsconfig.json",

  "compilerOptions": {
    ...
  }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 将通过 React 脚手架创建的 TS 项目中的 src/react-app-env.d.ts 拷贝到自己项目的 src 目录下
  • 重启项目
  • 说明
    • 项目中使用 TS 时,既可以包含 js 文件,又可以包含 ts 文件
      • .js.jsx(使用 JS 时,React 组件对应的文件后缀)
      • .ts.tsx(使用 TS 时,React 组件对应的文件后缀)、.d.ts
    • 在已有项目中,添加 TS 时的推荐模式
      • 新的功能用 TS
      • 已实现的功能,可以继续保持 JS 文件,慢慢修改为 TS 即可
    • React 组件对应的文件后缀,修改为:.tsx
    • 工具函数对应的文件后缀,修改为:.ts 或者为其添加类型声明文件 .d.ts
本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
推荐阅读
  

闽ICP备14008679号