TypeScript学习笔记

TypeScript学习笔记

小叶子

封面作者:NOEYEBROW

请先阅读JavaScript学习笔记

简介

  • TypeScriptJavaScript 的一个超集, 支持静态类型检查, 可以在编译时发现并纠正错误
  • tscTypeScript 的编译器, 可以将 TypeScript 代码编译成 JavaScript 代码
  • tsconfig.jsonTypeScript 的配置文件, 可以使用 tsc --init 命令生成默认配置文件
  • 在后端, DenoBun、最新的 Node.js 等环境已经内置了 TypeScript 编译器和配置, 可以直接运行 TypeScript 代码
  • 在前端, Vite 等打包工具会自动执行 TypeScript 编译, 一般不需要手动配置
  • TypeScript 有类型推断机制, 因此现有的 JavaScript 代码只需少量修改即可迁移到 TypeScript
1
2
3
4
5
6
7
8
# 如果使用 deno
deno run index.ts
# 如果使用 bun
bun run index.ts
# 如果使用 Node.js
npm i -g typescript # 也可以使用其他包管理器 / 安装到项目目录而不是全局
tsc index.ts # 如果是局部安装, 可以使用 npx tsc index.ts
node index.js

类型

TypeScript 可以使用 : 来定义变量的类型

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
// 基本类型
const any: any = 1 // 可以接受任意类型的值, 不推荐使用
const num: number = 1
const str: string = 'hello'
const bool: boolean = true
const nul: null = null
const und: undefined = undefined
const void: void = undefined
const sym: symbol = Symbol('symbol')
const big: bigint = 100n

// 复杂类型和函数
const arr: number[] = [1, 2, 3] // 或 : Array<number>
const tuple: [string, number] = ['leaf', 18] // 元组类型
const obj: { name: string, age: number } = { name: 'leaf', age: 18 }
const funExp: (a: number, b: number) => number = (a, b) => a + b
const voidFun: () => void = () => console.log('void')
function fun(a: number, b: number): number { return a + b }

// 枚举类型
const result: 'success' | 'error' = 'success'
// enum 关键字
enum Color { R = 'red', G = 'green', B = 'blue', Y = 'yellow' }
const favColor: Color = Color.Y
// 注意: 枚举类型会在编译后被转换成对象, 除非使用常量枚举
const enum Direction { Up, Down, Left, Right }
const dir: Direction = Direction.Up
// 常量枚举在编译后会被移除, 只保留值

// Promise
const promise: Promise<number> = new Promise((resolve, reject) => {
resolve(1)
})

unknown

unknown 类型是 any 类型的安全版本, 可以接受任意类型的值, 但不能直接操作, 需要先进行类型检查

1
2
3
4
5
6
let value: unknown
value = 1
// value += 1 // 报错: 对象的类型为 "unknown"。ts(2571)
if (typeof value === 'number') {
value += 1
}

never

never 类型表示永远不会返回结果的类型, 通常用于抛出异常或无限循环

1
2
3
4
5
6
function throwError(message: string): never {
throw new Error(message)
}
function infiniteLoop(): never {
while (true) {}
}

类型断言

TypeScript 可以使用 as 关键字来断言变量的类型; 断言只在编译阶段起作用, 不会影响运行时的类型

1
2
3
4
5
6
7
8
9
const arr: number[] = [0, 1, 2, 3]
const num = arr.find(n => n > 1) as number
// 如果不加上面的断言, num 的类型会被推断为 number | undefined
const absoulteNum: number = arr.find(n => n > 1) as number
// 如果不加上面的断言, 会报错: 类型 'number | undefined' 不能赋值给类型 'number'

// 或者使用尖括号
const num = <number>arr.find(n => n > 1)
// 但会和 JSX 语法冲突, 不能在 .tsx 中使用

非空断言

TypeScript 还提供了非空断言操作符 !, 可以用来断言一个值不为 nullundefined

1
2
3
4
const element = document.getElementById('app')!
// 如果不加上面的断言, element 的类型会被推断为 HTMLElement | null
// 会报错: 对象可能为 "null"。ts(2531)
element.innerHTML = 'Hello, TypeScript!'

接口

TypeScript 可以使用 interfacetype 来定义对象的成员及其类型, 二者区别见示例; 我一般在定义某个多次使用的对象时使用 type, 定义类、函数 props 时使用 interface

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 定义接口
interface Person {
name: string
age: number
// 可选成员
gender?: 'male' | 'female' | 'other'
phone?: string
// 只读成员, 类似于 const
readonly id: number
// 动态成员, 可以接受任意数量的成员, 但必须是 string 类型
[hobby: string]: string
}
// 使用接口
const leaf: Person = { name: 'leaf', age: 18, id: 1 }
function sayHi(person: Person): void {
console.log(`Hi, I'm ${person.name}, I'm ${person.age} years old`)
}
sayHi(leaf)

TypeScript 中的类和 ES6 中的类基本一致, 但有一些额外的特性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Person {
// ts 中, 实例成员必须在构造函数外指定类型
name: string
age: number
readonly id: number = 1
// 构造函数
constructor(name: string, age: number) {
this.name = name
this.age = age
}
// 实例方法
sayHi(): void {
console.log(`Hi, I'm ${this.name}, I'm ${this.age} years old`)
}
}
  • TypeScript 中的类可以用 publicprivateprotected 来修饰成员:
    public: 默认, 所有地方都可以访问, 相当于 ES6 中的类成员
    private: 只能在类的内部访问, 相当于 ES6 中的 #
    protected: 只能在类的内部和子类中访问
  • class 中也可以使用 readonly 修饰成员, 表示只读

类的接口

多个类可能具有一些共同的特性, 但这些特性之间的实现又有一些小差异, 此时用父类可能不太合适, 可以使用接口来抽象这些共同的特性; TypeScript 中, 可以使用 implements 来在类中实现接口

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
// 定义接口
interface Eat {
eat(food: string): void
}
interface Say {
sayHi(): void
}
// 使用接口
class Cat implements Eat {
name: string
constructor(name: string) {
this.name = name
}
eat(food: string): void {
console.log(`喵喵喵, ${this.food}, 喵喵喵喵喵喵!`)
}
}
// 使用多个接口
class Person implements Eat, Say {
name: string
age: number
constructor(name: string, age: number) {
this.name = name
this.age = age
}
eat(food: string): void {
console.log(`${this.name} is eating ${food}`)
}
sayHi(): void {
console.log(`Hi, I'm ${this.name}, I'm ${this.age} years old`)
}
}

抽象类

抽象类类似于上面的类的接口, 是多个类具有的一些公共方法, 但抽象类可以指定一些方法的实现, 而接口只能定义方法的签名; 抽象类使用 abstract 关键字来定义, 使用 extends 来继承

注意: 抽象类不能被实例化, 只能被继承

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
// 定义抽象类
abstract class Animal {
// 抽象方法, 和接口中的方法签名一样
abstract eat(food: string): void
abstract sayHi(): void
// 抽象类中可以定义非抽象方法, 指定默认实现
sleep(): void {
console.log('sleeping')
}
}
// 使用抽象类
class Cat extends Animal {
name: string
constructor(name: string) {
super() // 继承抽象类时, 必须在构造函数中调用 super
this.name = name
}
eat(food: string): void {
console.log(`喵喵喵, ${food}, 喵喵喵喵喵喵!`)
}
sayHi(): void {
console.log(`喵喵喵喵喵!`)
}
}
const cat = new Cat('Tom')
cat.eat('fish') // 喵喵喵, fish, 喵喵喵喵喵喵!
cat.sayHi() // 喵喵喵喵喵!
cat.sleep() // sleeping

泛型

泛型指的是在定义函数、接口或类的时候, 不预先指定具体的类型, 而在使用的时候再指定类型的一种特性; 定义函数或类时, 可以使用 <T> 来定义泛型类型, T 可以是任意标识符, 相当于一个额外的参数, 用来接受传入的类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 定义泛型函数
// 创建一个指定长度和值的数组
function createArray<T>(length: number, value: T): T[] {
return Array.from({ length }, () => value)
}
// 使用泛型函数
const arr1 = createArray<string>(3, 'x') // ['x', 'x', 'x']
const arr2 = createArray<number>(3, 1) // [1, 1, 1]
const arr3 = createArray(3, 1) // [1, 1, 1], 根据传入的参数类型推断泛型类型

// 泛型的默认值 (一般不必指定, 会根据传入的参数类型推断)
function generateArray<T = number>(length: number, value: T): T[] {
return Array.from({ length }, () => value)
}
class Stack<T = number> {
#items: T[] = []
push(item: T): void {
this.#items.push(item)
}
pop(): T | undefined {
return this.#items.pop()
}
}

typeof

typeof 关键字可以用来获取变量的类型, 用于约束泛型类型; 不同于 JavaScript 中的 typeof 运算符 (返回字符串), TypeScript 中的 typeof 返回变量的类型

1
2
3
4
function add(a: number, b: number): number {
return a + b
}
type Add = typeof add // (a: number, b: number) => number

extends

extends 关键字可以用来约束泛型的类型, 使泛型必须继承某个类或实现某个接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 简单条件判断
type TypeName<T> = T extends string ? 'string' : 'other'
const str: TypeName<string> = 'string'
const num: TypeName<number> = 'other'

// 多次条件判断
type TypeName<T> = T extends string ? 'string' : T extends number ? 'number' : 'other'
const str: TypeName<string> = 'string'
const num: TypeName<number> = 'number'
const other: TypeName<boolean> = 'other'

// 约束泛型类型
function getLength<T extends { length: number }>(arg: T): number {
return arg.length
}
getLength('hello') // 5
getLength([1, 2, 3]) // 3
getLength({ length: 5 }) // 5

keyof

keyof 关键字可以用来获取对象的所有键, 用于约束泛型类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 获取对象的所有键
type Person = {
name: string
age: number
}
type PersonKey = keyof Person // 'name' | 'age'

// 约束泛型类型
function getValue<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key]
}
const obj = { name: 'leaf', age: 18 }
const name: string = getValue(obj, 'name')
const age: number = getValue(obj, 'age')

infer

infer 关键字可以用来推断泛型类型, 通常用于条件判断

1
2
3
4
5
6
7
8
9
10
11
// 获取函数返回值类型
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : any
function add(a: number, b: number): number {
return a + b
}
type AddReturnType = ReturnType<typeof add> // number

// 获取 Promise 返回值类型
type UnwrapPromise<T> = T extends Promise<infer R> ? R : T
type PromiseReturnType = UnwrapPromise<Promise<number>> // number
type NormalReturnType = UnwrapPromise<number> // number

类型声明

一些第三方库的函数可能没有提供类型声明文件, 可以使用 declare 关键字来定义类型声明文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 引入第三方库
import { camelCase } from 'lodash'
// 定义类型声明文件
declare function camelCase(input: string): string

// 也可以对某个模块进行类型声明
import * as _ from 'lodash'
declare module 'lodash' {
export function camelCase(input: string): string
export function kebabCase(input: string): string
export function snakeCase(input: string): string
export function upperFirst(input: string): string
// ...
}

一些常见第三方库已经有了类型声明文件, 可以直接安装即可, 如 npm i @types/node -D(还有些库的声明文件是内置的, 不需要安装, 如下所示)

.d.ts 文件

TypeScript 的类型声明文件通常以 .d.ts 为后缀, 用来声明全局变量、函数、类等

其中 /// <reference types="xxx" /> 用来引入其他类型声明文件, /// <reference path="xxx" /> 用来引入其他文件

函数重载

函数重载指的是为同一个函数提供多个函数类型定义, 以便在不同的情况下使用不同的类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 定义函数重载
function reverse(x: number): number
function reverse(x: string): string
// 实现函数
function reverse(x: number | string): number | string {
if (typeof x === 'number') {
return Number(x.toString().split('').reverse().join(''))
} else if (typeof x === 'string') {
return x.split('').reverse().join('')
}
}
// 使用函数
reverse(123) // 321
reverse('hello') // 'olleh'

工具类型

TypeScript 内置了一些工具类型, 可以用来操作类型

工具类型 描述
Partial<T> 将类型 T (对象) 中的所有属性设置为可选
Required<T> 将类型 T (对象) 中的所有属性设置为必选
Readonly<T> 将类型 T (对象) 中的所有属性设置为只读
Record<K, T> 可以简化对象的类型定义
Pick<T, K> 从类型 T (对象) 中选取部分属性
Omit<T, K> 从类型 T (对象) 中排除部分属性
Exclude<T, U> 从类型 T (联合类型) 中排除可以赋值给 U 的类型
Extract<T, U> 从类型 T (联合类型) 中提取可以赋值给 U 的类型
NonNullable<T> 从类型 T (联合类型) 中排除 nullundefined
ReturnType<T> 获取类型 T (函数) 的返回值类型
Parameters<T> 获取类型 T (函数) 的参数类型 [...]
Awaited<T> 获取类型 T (Promise) 的返回值类型
ConstructorParameters<T> 获取类型 T (类) 的构造函数参数类型 [...]
InstanceType<T> 获取类型 T (类) 的实例类型

还有几个和 this 相关的工具类型, 这里不做介绍

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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
type Gender = 'male' | 'female' | 'other'
type Person = { name: string, age: number, gender?: Gender }

// Partial<T> 将类型 T 中的所有属性设置为可选
type PartialPerson = Partial<Person>
const personA: PartialPerson = { name: 'leaf' }
const personB: PartialPerson = {}

// Required<T> 将类型 T 中的所有属性设置为必选
type RequiredPerson = Required<PartialPerson>
const personC: RequiredPerson = { name: 'leaf', age: 18, gender: 'male' }

// Readonly<T> 将类型 T 中的所有属性设置为只读
type ReadonlyPerson = Readonly<Person>
const personD: ReadonlyPerson = { name: 'leaf', age: 18 }

// Record<K, T> 可以简化对象的类型定义
type RecordPerson = Record<'name' | 'age', string> // { name: string, age: string }

// Pick<T, K> 从类型 T 中选取部分属性
type PickPerson = Pick<Person, 'name'> // { name: string }

// Omit<T, K> 从类型 T 中排除部分属性
type OmitPerson = Omit<Person, 'name'> // { age: number, gender?: Gender }

// Exclude<T, U> 从类型 T 中排除可以赋值给 U 的类型
type TwoGender = Exclude<Gender, 'other'> // 'male' | 'female'

// Extract<T, U> 从类型 T 中提取可以赋值给 U 的类型
type ExtractGender = Extract<Gender, 'male' | 'female'> // 'male' | 'female'

// NonNullable<T> 从类型 T 中排除 null 和 undefined
type NonNullablePerson = NonNullable<Person | null | undefined> // Person

// ReturnType<T> 获取函数 T 的返回值类型
type AddReturnType = ReturnType<(a: number, b: number) => number> // number

// Parameters<T> 获取函数 T 的参数类型
type AddParameters = Parameters<(a: number, b: number) => number> // [number, number]

// Awaited<T> 获取 Promise T 的返回值类型
type PromiseReturnType = Awaited<Promise<number>> // number

// InstanceType<T> 获取构造函数 T 的实例类型
class PersonClass {
name: string
constructor(name: string) {
this.name = name
}
}
type PersonInstance = InstanceType<typeof PersonClass> // PersonClass
// 也可以直接把类名作为类型
const person: PersonInstance = new PersonClass('leaf')
const person: PersonClass = new PersonClass('leaf')

// ConstructorParameters<T> 获取构造函数 T 的参数类型
type PersonConstructorParameters = ConstructorParameters<typeof PersonClass> // [string]

模版字符串

TypeScript 5+ 中可以使用类似于模版字符串的语法来定义字符串类型

工具类型 描述
Uppercase<StringType> 将字符串转换为大写
Lowercase<StringType> 将字符串转换为小写
Capitalize<StringType> 将字符串的首字母转换为大写
Uncapitalize<StringType> 将字符串的首字母转换为小写
1
2
3
4
5
6
7
8
9
10
type HexColor = `#${string}`
type RGBColor = `rgb(${number}, ${number}, ${number})`
type Color = Uppercase<HexColor> | Lowercase<RGBColor>
type Colors = { [key: `color-${string}`]: Color }

const colors: Colors = {
'color-red': '#FF0000',
'color-green': 'rgb(0, 255, 0)',
'color-blue': '#0000FF',
}
  • 标题: TypeScript学习笔记
  • 作者: 小叶子
  • 创建于 : 2024-03-06 08:57:02
  • 更新于 : 2025-10-13 09:30:54
  • 链接: https://blog.leafyee.xyz/2024/03/06/TypeScript/
  • 版权声明: 版权所有 © 小叶子,禁止转载。
评论