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
}

泛型默认类型用于在调用方没有显式传入类型参数时提供兜底类型。相比 anyunknown 更安全,因为使用前仍然需要做类型判断或类型收窄。

泛型约束

// 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

非空断言会跳过 nullundefined 检查。如果值可能为空,应优先使用条件判断或可选链。

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