TypeScript 核心概念、类型系统
2023-03-02
之前的分类有点多了, 让 ai 重新给整合了一些
类型基础
核心特性
- 编译时检查:大部分类型错误会在编译阶段暴露,而不是等到运行时。
- 静态类型:变量、函数参数、返回值等都可以在编写代码时声明类型。
- 结构化类型系统:TypeScript 主要根据对象结构判断类型是否兼容。
- 渐进式类型:可以从 JavaScript 平滑迁移,逐步补充类型。
基本类型
string | number | boolean | undefined | null | symbol | bigint | object
类型标注
const v1: number = 1 // 小写形式(推荐)
const v2: number = 1 // 大写形式表示包装对象类型,通常不推荐
字面量类型
let type: 'hello'
type = 'hello' // ✅
type = 'test' // ❌
类型拓宽与收窄
const a = 'a' // string
const aa = 'a' // 'a'(const 自动收窄)
const aaa: 'a' = 'a' // 'a'(显式收窄)
联合类型
const direction: 'LEFT' | 'RIGHT' = 'LEFT'
接口
interface User {
name: string // 必需
age?: number // 可选
readonly id: string // 只读
}
交叉类型
type C = A & B // 同时满足 A 和 B
type D = A | B // 满足 A 或 B
交叉类型使用 &,表示一个值需要同时具备多个类型的能力;联合类型使用 |,表示一个值可以是多个类型中的任意一种。
特殊类型
function fn1() {
} // void
function fn2() { // undefined
return undefined
}
⚠️ 尽量避免使用
any,优先使用unknown
函数类型
函数签名
type Sum = (a: number, b: number) => number
const sum: Sum = (a, b) => a + b
函数重载
// 重载签名
function calculateArea(length: number, width: number): number
function calculateArea(length: string, width: string): string
// 实现签名
function calculateArea(length: number | string, width: number | string): number | string {
if (typeof length === 'number' && typeof width === 'number') {
return length * width
}
if (typeof length === 'string' && typeof width === 'string') {
return (Number.parseFloat(length) * Number.parseFloat(width)).toString()
}
throw new Error('参数类型错误')
}
calculateArea(5, 10) // 50
calculateArea('5', '10') // "50"
⚠️ 重载签名与实现必须连续放在一起
泛型
泛型基础
function fn<T>(value: T): T {
return value
}
fn(42) // number
fn('hello') // string
接口中的泛型
interface ResultData<T> {
code: number
message: string
data: T
}
多泛型
function swap<T, U>(tuple: [T, U]): [U, T] {
return [tuple[1], tuple[0]]
}
泛型默认类型
interface ResultData<T = unknown> {
data: T
}
泛型默认类型用于在调用方没有显式传入类型参数时提供兜底类型。相比 any,unknown 更安全,因为使用前仍然需要做类型判断或类型收窄。
泛型约束
// extends 约束
function getLength<T extends { length: number }>(value: T): number {
return value.length
}
// keyof 约束
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key]
}
类型断言与类型满足
类型断言
const inputDom = document.querySelector('input');
(inputDom as HTMLInputElement).value
类型断言用于告诉 TypeScript “我比你更清楚这个值的类型”。它不会做运行时校验,因此应该只在确实能保证类型正确时使用。
非空断言
const s = getRandom(6)
s!.length // 确定不为 null/undefined
非空断言会跳过 null 和 undefined 检查。如果值可能为空,应优先使用条件判断或可选链。
satisfies 操作符
// 使用 as:强制断言,可能掩盖缺失字段
const t1 = {} as IConfig
// 使用 satisfies:检查是否满足目标类型,同时保留字面量类型
const t2 = { test: 123 } satisfies IConfig
💡 需要校验对象是否符合某个类型时,优先考虑
satisfies,不要直接用as跳过检查。
高级类型
typeof 类型推导
type Temp1 = typeof temp1 // string
type User = typeof user // 完整对象类型
索引访问类型
type Age = User['age'] // number
type Color = typeof colors[number] // string
keyof 操作符
type UserKeys = keyof User // "id" | "name" | "age"
type UserValues = User[keyof User] // string | number
映射类型
type User = {
[key in 'name' | 'age' | 'id']: string
}
映射类型通过 in 遍历联合类型中的每个成员,并为每个成员生成对应的属性。
条件类型
type IsString<T> = T extends string ? true : false
type ExtractString<T> = T extends string ? T : never
infer 类型推断
type GetReturnType<T> = T extends (...args: any[]) => infer R ? R : never
type GetParameters<T> = T extends (...args: infer P) => any ? P : never
type UnpackPromise<T> = T extends Promise<infer P> ? P : never
工具类型
常用工具类型
| 类型 | 作用 |
|---|---|
Partial<T> | 属性可选 |
Required<T> | 属性必填 |
Readonly<T> | 属性只读 |
Pick<T, K> | 保留指定属性 |
Omit<T, K> | 排除指定属性 |
Record<K, V> | 创建映射类型 |
Extract<T, U> | 提取联合类型 |
Exclude<T, U> | 排除联合类型 |
ReturnType<T> | 获取返回值类型 |
Parameters<T> | 获取参数类型 |
工具类型实现原理
// Partial
type MyPartial<T> = { [K in keyof T]?: T[K] }
// Omit
type MyOmit<T, K> = { [P in Exclude<keyof T, K>]: T[P] }
// Exclude
type MyExclude<T, U> = T extends U ? never : T
实用模式
模板字符串类型
type BoxModel = `${'padding' | 'margin'}-${'left' | 'right'}`
// "padding-left" | "padding-right" | "margin-left" | "margin-right"
深度操作
type DeepPartial<T> = {
[K in keyof T]?: T[K] extends object ? DeepPartial<T[K]> : T[K]
}
type DeepReadonly<T> = {
readonly [K in keyof T]: T[K] extends object ? DeepReadonly<T[K]> : T[K]
}
常量对象替代枚举
const Status = {
Success: 200,
NotFound: 404
} as const
type Status = typeof Status[keyof typeof Status] // 200 | 404