know

typescript

高优先级知识点,需要熟练掌握。

# 1. TypeScript 中 interface 和 type 的差别

相同点:都可定义对象类型、继承(interface extends / type &)。 不同点:

  1. interface 可重复声明并自动合并type 不可重复声明
  2. type 可定义基本类型、联合类型、元组等,interface 只能定义对象 / 函数类型
  3. 类可通过 implements 实现 interface,但不能实现 type(除非 type 是对象类型)。
  • interface 用于定义对象的结构,适合描述类、对象、函数等,可以多次声明同名接口自动合并,支持继承和实现。
  • type 用于类型别名,可以定义任意类型(基本类型、联合类型、交叉、元组类型等),不能自动合并,但可以通过交叉类型扩展。

# 1.1. interface 示例

interface Person {
  name: string
  age: number
}

interface User extends Person {
  gender: number
}

// 合并后:{ name: string; age: number; gender: number }

# 1.2. type 示例

type Point = { x: number; y: number }
type PartialPoint = Point | null
type Name = string | number

// 当你想获取一个变量的类型时,type 语句中还可以使用 typeof 获取实例的 类型进行赋值
let div = document.createElement('div')
type B = typeof div // HTMLElement

# 1.3. 总结

  • interface 更适合面向对象和扩展
  • 能用 interface 实现,就用 interface , 如果不能就用 type ,type 更灵活,能表示任意类型

# 2. typescript 中的 is 关键字有什么用?

is 关键字用于类型保护

# 3. TypeScript 中的 Declare 关键字有什么用?

声明全局变量或类型、模块或函数等实体的类型信息

// 这是外部 JS 代码,TypeScript 无法直接识别
window.globalConfig = {
  apiUrl: 'https://example.com',
  debug: true
}
// 声明全局变量的类型
declare var globalConfig: {
  apiUrl: string
  debug: boolean
}
// 声明全局命名空间 MyLib
declare namespace MyLib {
  interface User {
    name: string
    age: number
  }
  function getUser(): User
}

// 使用时 TypeScript 能识别命名空间内的类型
const user: MyLib.User = MyLib.getUser()
// 声明全局接口
declare interface Window {
  // 为 window 对象扩展一个自定义属性
  appVersion: string
}

// 使用时可直接访问
window.appVersion = '1.0.0' // 正确,TypeScript 认可该属性
// 新建 xxx-utils.d.ts(.d.ts 是类型声明文件)
declare module 'xxx-utils' {
  export function formatDate(date: Date): string
  export const version: string
}

// 在 TS 中导入使用时,会自动识别类型
import { formatDate } from 'xxx-utils'
formatDate(new Date()) // 正确,参数和返回值类型被识别

TypeScript 支持的访问修饰符有哪些?

  1. public: 公共的,可以在任何地方访问
  2. private: 私有的,只能在当前类中访问
  3. protected: 受保护的,只能在当前类和子类中访问
  4. readonly: 只读的,只能在声明时初始化,之后不能修改
  5. static 关键字定义静态属性和方法,属于类本身而不是实例。

TypeScript 中的类型别名有什么用?

类型别名可以给一个类型起一个新的名字,方便使用。

type Name = string
type NameResolver = () => string
type NameOrResolver = Name | NameResolver

function getName(name: NameOrResolver): string {
  if (typeof name === 'string') {
    return name
  } else {
    return name()
  }
}

getName('Alice') // 'Alice'
getName(() => 'Bob') // 'Bob'

TypeScript 中的联合类型有什么用?

联合类型可以表示一个值可以是多种类型中的一种。

type NameOrAge = string | number

function greet(nameOrAge: NameOrAge) {
  if (typeof nameOrAge === 'string') {
    console.log(`Hello, ${nameOrAge}!`)
  } else {
    console.log(`You are ${nameOrAge} years old.`)
  }
}

greet('Alice') // Hello, Alice!
greet(25) // You are 25 years old.

TypeScript 中的交叉类型有什么用?

交叉类型可以将多个类型合并为一个类型。

type Person = { name: string }
type Employee = { salary: number }
type PersonAndEmployee = Person & Employee

function work(personAndEmployee: PersonAndEmployee) {
  console.log(
    `Hello, ${personAndEmployee.name}. Your salary is ${personAndEmployee.salary}.`
  )
}

const person: Person = { name: 'Alice' }
const employee: Employee = { salary: 50000 }

work(person) // Hello, Alice. Your salary is undefined.
work(employee) // Hello, undefined. Your salary is 50000.

# 4. TypeScript 中的索引类型有什么用?

索引类型可以访问对象中元素的类型。

interface Map<T> {
  [key: string]: T
}

const map: Map<number> = {
  a: 1,
  b: 2,
  c: 3
}

function getValue(key: keyof Map<number>) {
  return map[key]
}

getValue('a') // 1
getValue('b') // 2
getValue('c') // 3

# 5. TypeScript 中的条件类型有什么用?

条件类型可以根据条件判断来确定类型。

type IsString<T> = T extends string ? true : false

function isString(value: unknown): IsString<typeof value> {
  return typeof value === 'string'
}

type IsNumber<T> = T extends number ? true : false

Typescript 中 never 和 void 的区别?

never never 表示函数根本不会执行完成(永远不会到达终点),通常用于描述 “函数会抛出异常” 或 “进入无限循环”

void 用于表示函数没有返回值。没有副作用

# 6. Typescript 中什么是类型接口?

在 TypeScript 中,类类型接口(Class Type Interface) 是一种特殊的接口,用于约束类的结构,规定类必须实现哪些属性和方法。它类似于 “类的契约”,定义了类应该遵循的规范,包括必须包含的成员(属性、方法)及其类型。

一个类可以同时实现多个接口(用逗号分隔),需满足所有接口的约束:

interface Runable {
  run(): void
}

interface Swimmable {
  swim(): void
}

// 同时实现两个接口
class Duck implements Runable, Swimmable {
  run(): void {
    console.log('鸭子跑步')
  }

  swim(): void {
    console.log('鸭子游泳')
  }
}

# 7. 联合类型与交叉类型

联合类型(Union):值可以是多种类型之一,例如 string | number 交叉类型(Intersection):值必须同时满足多个类型,例如 Person & Employee

# 8. 类型别名与接口的区别

类型别名(Type Alias):是一种新的类型,可以给一个现有类型定义一个新的名称。

接口(Interface):是一种类型,用于描述一个对象的结构。它定义了属性、方法、事件等。

# 9. 什么是泛型?

泛型(Generics)是指在定义函数、接口或类时,不预先指定具体的类型,而是使用类型参数(Type Parameter)来表示。

function identity<T>(arg: T): T {
  return arg
}

identity('hello') // 'hello'
identity(25) // 25

# 10. TypeScript 中的类型有哪些?

TypeScript 支持多种类型,主要包括:

# 10.1. 基本类型(Primitive Types)

  • number:数字类型
  • string:字符串类型
  • boolean:布尔类型
  • null:空值
  • undefined:未定义
  • symbol:符号类型
  • bigint:大整数类型

# 10.2. 对象类型(Object Types)

  • Object:对象类型
  • Array:数组类型,如 number[]
  • Tuple:元组类型,如 [string, number]
  • Function:函数类型

# 10.3. 枚举类型(Enum)

用于定义一组命名常量。

enum Direction {
  Up,
  Down,
  Left,
  Right
}

# 10.4. 接口类型(Interface)

用于描述对象的结构。

interface Person {
  name: string
  age: number
}

# 10.5. 类型别名(Type Alias)

为类型定义别名。

type Name = string
type Point = { x: number; y: number }

# 10.6. 联合类型(Union Type)

一个值可以是多种类型之一。

type Result = string | number

# 10.7. 交叉类型(Intersection Type)

一个值同时满足多个类型。

type Employee = Person & { salary: number }

# 10.8. 字面量类型(Literal Type)

指定具体的值作为类型。

type Gender = 'male' | 'female'

# 10.9. 泛型类型(Generics)

定义时不指定具体类型,使用时再指定。

function identity<T>(arg: T): T {
  return arg
}

# 10.10. any 类型

表示任意类型,关闭类型检查。

let value: any = 123
value = 'abc'

# 10.11. unknown 类型

表示未知类型,比 any 更安全。

let input: unknown

# 10.12. never 类型

表示永远不会有值的类型,如抛异常或无限循环。

function error(): never {
  throw new Error('error')
}

# 10.13. void 类型

表示没有返回值的类型,常用于函数。

function log(msg: string): void {
  console.log(msg)
}

TypeScript 类型系统丰富,能够满足各种场景下的类型需求,提升代码的安全性和可维护性。

# 11. 内置工具类型(Utility Types)

TypeScript 提供了一些常用的内置工具类型,用于对已有类型进行变换和组合,提升类型系统的灵活性和表达能力。常见的工具类型如下:

# 11.1. Partial

将类型 T 的所有属性变为可选。

interface User {
  name: string
  age: number
}

type PartialUser = Partial<User>
// 等价于:{ name?: string; age?: number }

# 11.2. Required

将类型 T 的所有属性变为必选。

type RequiredUser = Required<PartialUser>
// 等价于:{ name: string; age: number }

# 11.3. Readonly

将类型 T 的所有属性变为只读。

type ReadonlyUser = Readonly<User>
// 等价于:{ readonly name: string; readonly age: number }

# 11.4. Pick<T, K>

从类型 T 中挑选部分属性组成新类型。

type NameOnly = Pick<User, 'name'>
// 等价于:{ name: string }

# 11.5. Omit<T, K>

从类型 T 中排除某些属性组成新类型。

type WithoutAge = Omit<User, 'age'>
// 等价于:{ name: string }

# 11.6. Record<K, T>

构造一个以 K 为键、T 为值的对象类型。

type UserMap = Record<string, User>
// 等价于:{ [key: string]: User }

# 11.7. Exclude<T, U>

从 T 类型中排除 U 类型的成员。

type T = 'a' | 'b' | 'c'
type T1 = Exclude<T, 'a' | 'b'>
// 结果:'c'

# 11.8. Extract<T, U>

从 T 类型中提取 U 类型的成员。

type T2 = Extract<T, 'a' | 'b'>
// 结果:'a' | 'b'

# 11.9. NonNullable

去除类型 T 中的 null 和 undefined。

type T3 = string | number | null | undefined
type T4 = NonNullable<T3>
// 结果:string | number

# 11.10. ReturnType

获取函数类型 T 的返回值类型。

function fn(): number {
  return 123
}
type FnReturn = ReturnType<typeof fn>
// 结果:number

# 11.11. Parameters

获取函数类型 T 的参数类型组成的元组。

function greet(name: string, age: number): void {}
type GreetParams = Parameters<typeof greet>
// 结果:[string, number]

# 11.12. ConstructorParameters

获取构造函数类型 T 的参数类型组成的元组。

class Person {
  constructor(name: string, age: number) {}
}
type PersonParams = ConstructorParameters<typeof Person>
// 结果:[string, number]

# 11.13. InstanceType

获取构造函数类型 T 的实例类型。

type PersonInstance = InstanceType<typeof Person>
// 结果:Person

# 12. 如何检查 TypeScript 中的 null 和 undefined?

在 TypeScript 中,可以通过以下几种方式检查变量是否为 nullundefined

# 12.1. 使用严格等于(===)

let value: string | null | undefined

if (value === null) {
  // value 是 null
}

if (value === undefined) {
  // value 是 undefined
}

# 12.2. 使用 == 判断(同时检查 null 和 undefined)

if (value == null) {
  // value 可能是 null 或 undefined
}

# 12.3. 使用 typeof 检查 undefined

if (typeof value === 'undefined') {
  // value 是 undefined
}

# 12.4. 可选链和空值合并运算符

const result = value ?? '默认值' // value 为 null 或 undefined 时返回 '默认值'

# 12.5. 类型保护

TypeScript 的类型保护可以自动缩小类型范围:

function printLength(str?: string | null) {
  if (str != null) {
    // 这里 str 类型被缩小为 string
    console.log(str.length)
  }
}

# 13. 什么是 TypeScript 的方法重载?

方法重载是指为同一个函数声明多个不同的参数类型或数量的签名,调用时根据参数自动匹配对应的签名。TypeScript 只允许一个实现体,多个类型声明。 方法重载让函数能处理多种参数类型,提升灵活性。

function add(a: number, b: number): number
function add(a: string, b: string): string
function add(a: any, b: any): any {
  return a + b
}

add(1, 2) // 3
add('a', 'b') // 'ab'

# 14. TypeScript 中什么是装饰器?它们可以应用于什么?

装饰器(Decorator)是 TypeScript 提供的一种特殊语法,用于在类、方法、属性、参数等声明上添加元数据或扩展功能。装饰器本质上是一个函数,可以在编译时对被修饰的目标进行修改或增强。 装饰器常用于依赖注入、日志、权限校验等场景,提升代码的可扩展性和可维护性。

# 14.1. 应用场景

装饰器可以应用于以下几种:

  1. 类装饰器:用于修饰类本身
  2. 方法装饰器:用于修饰类的方法
  3. 属性装饰器:用于修饰类的属性
  4. 参数装饰器:用于修饰方法的参数

# 14.2. 示例

// 类装饰器
function LogClass(target: Function) {
  console.log('类被装饰:', target.name)
}

@LogClass
class Person {}

// 方法装饰器
function LogMethod(
  target: any,
  propertyKey: string,
  descriptor: PropertyDescriptor
) {
  console.log('方法被装饰:', propertyKey)
}

class Animal {
  @LogMethod
  speak() {}
}

注意:使用装饰器需要在 tsconfig.json 中开启 "experimentalDecorators": true

# 15. 如何使用 TypeScript 的 Mixin

Mixin 是一种复用类功能的模式,可以将多个类的功能“混入”到一个目标类中。TypeScript 支持通过函数和类型组合实现 Mixin。

# 15.1. 基本用法

  1. 定义一个 Mixin 函数,接收一个类并返回一个扩展后的新类:
type Constructor<T = {}> = new (...args: any[]) => T

function Timestamped<TBase extends Constructor>(Base: TBase) {
  return class extends Base {
    timestamp = Date.now()
  }
}
  1. 定义基础类:
class User {
  name: string
  constructor(name: string) {
    this.name = name
  }
}
  1. 使用 Mixin 生成新类:
const TimestampedUser = Timestamped(User)
const user = new TimestampedUser('Alice')
console.log(user.name) // 'Alice'
console.log(user.timestamp) // 时间戳

# 15.2. 多个 Mixin 组合

可以链式组合多个 Mixin:

function Serializable<TBase extends Constructor>(Base: TBase) {
  return class extends Base {
    serialize() {
      return JSON.stringify(this)
    }
  }
}

const MixedUser = Serializable(Timestamped(User))
const user2 = new MixedUser('Bob')
console.log(user2.serialize())

总结:TypeScript 的 Mixin 通过高阶函数和类型约束实现,可以灵活复用和组合类的功能。

# 16. 类型断言

优先使用 as 语法

const value = 'hello' as string
const element = document.getElementById('app') as HTMLElement

# 17. TypeScript 中的模块是什么?

模块是 TypeScript 中用于组织和复用代码的机制。模块可以包含变量、函数、类、接口等,并通过 export 导出、import 导入,实现跨文件共享和隔离作用域。 模块让 TypeScript 项目结构更清晰,代码复用更方便。

# 17.1. 特点

  • 每个文件默认就是一个模块
  • 通过 export 导出成员
  • 通过 import 导入其他模块成员

# 17.2. 示例

// math.ts
export function add(a: number, b: number): number {
  return a + b
}

// main.ts
import { add } from './math'
console.log(add(1, 2))

# 17.3. 分类

  1. 全局模块:直接在全局作用域声明,所有地方可访问
  2. 命名空间模块:使用 namespace 关键字声明,仅限当前文件
  3. ES 模块:使用 import/export,推荐使用,支持跨文件访问

# 18. 为什么推荐使用 TypeScript?

TypeScript 是 JavaScript 的超集,增加了类型系统和编译时检查,推荐使用 TypeScript 的原因如下:

  1. 类型安全 编译时检测错误,减少运行时 Bug。提高代码可维护性,类型定义即文档。

  2. 代码可读性和可维护性 明确的类型定义让代码更易理解和维护,便于团队协作。

  3. 智能提示和自动补全 编辑器可根据类型信息提供更智能的代码补全和重构支持。

  4. 更强的重构能力 类型系统让重构更安全,减少因改动导致的隐性错误。

  5. 支持最新和未来的 JavaScript 特性 TypeScript 支持 ES6+ 语法,并可编译为兼容旧环境的代码。

  6. 大型项目和团队开发的最佳选择 类型约束和接口设计让大型项目结构更清晰,开发更高效。

# 19. 如何避免 any 类型的滥用?

  • 优先使用具体类型、联合类型或 unknown。
  • 对复杂对象定义接口 / 类型别名。
  • 开启 strict: true 强制类型检查。

# 20. TypeScript 内置数据类型

# 20.1. 基础类型(Primitive Types)

  • boolean: 布尔值,truefalse
  • number: 数字,包括整数、浮点数、二进制、八进制、十六进制
  • string: 字符串,单引号、双引号、模板字符串
  • bigint: 大整数,用于表示任意精度的整数
  • symbol: 唯一标识符,用于创建对象的唯一属性键
  • undefined: 未定义值
  • null: 空值
let isDone: boolean = false
let count: number = 42
let name: string = 'TypeScript'
let bigNumber: bigint = 123n
let sym: symbol = Symbol('id')
let u: undefined = undefined
let n: null = null

# 20.2. 数组类型(Array Types)

  • T[]: 元素类型为 T 的数组
  • Array<T>: 泛型数组语法
let numbers: number[] = [1, 2, 3]
let strings: Array<string> = ['a', 'b', 'c']
let mixed: (string | number)[] = ['hello', 42]

# 20.3. 元组类型(Tuple Types)

  • 固定长度和类型的数组
let tuple: [string, number] = ['hello', 42]
let optionalTuple: [string, number?] = ['hello'] // 可选元素
let restTuple: [string, ...number[]] = ['hello', 1, 2, 3] // 剩余元素

# 20.4. 对象类型(Object Types)

  • object: 非原始值类型
  • {}: 空对象类型(可包含任何非 null/undefined 的值)
  • 接口(Interface): 定义对象结构
let obj: object = { x: 0 }
let emptyObj: {} = { x: 0 }

interface User {
  name: string
  age: number
  email?: string // 可选属性
  readonly id: number // 只读属性
}

# 20.5. 函数类型(Function Types)

  • 函数声明类型
  • 箭头函数类型
  • 函数重载
// 函数类型
let add: (x: number, y: number) => number = (x, y) => x + y

// 函数重载
function greet(name: string): string
function greet(age: number): string
function greet(value: string | number): string {
  return typeof value === 'string' ? `Hello ${value}` : `Age: ${value}`
}

# 20.6. 联合类型(Union Types)

  • 表示值可以是多种类型之一
let id: string | number = '123'
let status: 'loading' | 'success' | 'error' = 'loading'

# 20.7. 交叉类型(Intersection Types)

  • 将多个类型合并为一个类型
interface A {
  x: number
}
interface B {
  y: string
}
type C = A & B // { x: number; y: string; }

# 20.8. 字面量类型(Literal Types)

  • 具体的值作为类型
let direction: 'up' | 'down' | 'left' | 'right' = 'up'
let count: 1 | 2 | 3 = 1

# 20.9. 枚举类型(Enum Types)

  • 命名常量集合
enum Color {
  Red,
  Green,
  Blue
}
enum Status {
  Active = 1,
  Inactive = 0
}
enum Direction {
  Up = 'UP',
  Down = 'DOWN'
}

# 20.10. 泛型类型(Generic Types)

  • 类型参数化
interface Box<T> {
  value: T
}

let stringBox: Box<string> = { value: 'hello' }
let numberBox: Box<number> = { value: 42 }

# 20.11. 映射类型(Mapped Types)

  • 基于现有类型创建新类型
type Partial<T> = {
  [P in keyof T]?: T[P]
}

type Readonly<T> = {
  readonly [P in keyof T]: T[P]
}

# 20.12. 条件类型(Conditional Types)

  • 基于条件选择类型
type NonNullable<T> = T extends null | undefined ? never : T
type ApiResponse<T> = T extends string ? { message: T } : { data: T }

# 20.13. 工具类型(Utility Types)

  • Partial<T>: 所有属性变为可选
  • Required<T>: 所有属性变为必需
  • Readonly<T>: 所有属性变为只读
  • Record<K, V>: 键类型为 K,值类型为 V 的对象
  • Pick<T, K>: 从 T 中选择属性 K
  • Omit<T, K>: 从 T 中排除属性 K
  • Exclude<T, U>: 从 T 中排除可赋值给 U 的类型
  • Extract<T, U>: 从 T 中提取可赋值给 U 的类型
  • NonNullable<T>: 排除 null 和 undefined
  • Parameters<T>: 函数参数类型
  • ReturnType<T>: 函数返回类型
interface User {
  id: number
  name: string
  email: string
}

type PartialUser = Partial<User> // 所有属性可选
type UserEmail = Pick<User, 'email'> // { email: string }
type UserWithoutId = Omit<User, 'id'> // { name: string; email: string }

# 20.14. 特殊类型

  • any: 任意类型,关闭类型检查
  • unknown: 类型安全的 any,需要类型断言或类型守卫
  • never: 永不存在的值的类型
  • void: 函数无返回值
let anything: any = 42
let something: unknown = 'hello'
let neverValue: never = (() => {
  throw new Error()
})()
function noReturn(): void {
  console.log('hello')
}

# 20.15. 类型断言(Type Assertions)

  • 告诉编译器某个值的类型
let value: unknown = 'hello'
let str: string = value as string
let str2: string = <string>value // JSX 中不推荐

# 20.16. 类型守卫(Type Guards)

  • 运行时检查类型
function isString(value: unknown): value is string {
  return typeof value === 'string'
}

if (isString(value)) {
  // 在此块中,value 被推断为 string
  console.log(value.toUpperCase())
}

# 21. TypeScript 中的类型断言

什么是类型断言?

类型断言是告诉 TypeScript 编译器某个值的具体类型的一种方式。它不会在运行时改变值,只是告诉编译器"相信我,我知道这个值的类型"。

# 21.1. as 语法(推荐)

let value: unknown = 'Hello TypeScript'
let str: string = value as string
let num: number = (value as string).length

# 21.2. 尖括号语法(不推荐在 JSX 中使用)

let value: unknown = 'Hello TypeScript'
let str: string = <string>value
let num: number = (<string>value).length

# 21.2.1. 常见使用场景

# 21.3. 处理 any 类型

let someValue: any = 'this is a string'
let strLength: number = (someValue as string).length

# 21.4. 处理联合类型

function getLength(value: string | number): number {
  // 类型断言告诉编译器 value 是 string
  return (value as string).length
}

# 21.5. DOM 元素操作

let input = document.getElementById('myInput') as HTMLInputElement
input.value = 'Hello' // 现在可以安全访问 value 属性

// 或者
let input2 = <HTMLInputElement>document.getElementById('myInput')

# 21.6. 处理 unknown 类型

let userInput: unknown = getUserInput()
let userName: string = userInput as string

// 更安全的做法是结合类型守卫
if (typeof userInput === 'string') {
  let userName: string = userInput // 不需要类型断言
}

# 21.6.1. 类型断言 vs 类型转换

// 类型断言:编译时概念,运行时无影响
let value: unknown = '123'
let str: string = value as string // 运行时 value 仍然是 unknown

// 类型转换:运行时实际转换
let num: number = Number(value) // 运行时将字符串转换为数字

# 21.6.2. 双重断言(Double Assertion)

当类型断言不够直接时,可以使用双重断言:

let value: unknown = 'Hello'
// 错误:unknown 不能直接断言为 HTMLElement
// let element: HTMLElement = value as HTMLElement;

// 正确:先断言为 any,再断言为目标类型
let element: HTMLElement = value as any as HTMLElement

# 21.6.3. 类型断言与类型守卫的区别

// 类型断言:强制告诉编译器类型
function processValue(value: unknown) {
  let str = value as string // 强制断言,可能不安全
  console.log(str.toUpperCase())
}

// 类型守卫:运行时检查类型
function processValueSafe(value: unknown) {
  if (typeof value === 'string') {
    let str = value // 类型收窄,安全
    console.log(str.toUpperCase())
  }
}

# 21.6.4. 实际应用示例

# 21.7. API 响应处理

interface ApiResponse {
  data: any
  status: number
}

function handleApiResponse(response: unknown) {
  const apiResponse = response as ApiResponse
  if (apiResponse.status === 200) {
    console.log(apiResponse.data)
  }
}

# 21.8. 事件处理

function handleClick(event: Event) {
  const target = event.target as HTMLButtonElement
  target.disabled = true
}

# 21.9. 第三方库类型

// 假设第三方库没有类型定义
declare const someLibrary: any
const libraryInstance = someLibrary as {
  init: () => void
  destroy: () => void
}

# 21.9.1. 最佳实践

# 21.10. 优先使用类型守卫

// 不推荐:直接断言
function badExample(value: unknown) {
  return (value as string).toUpperCase()
}

// 推荐:类型守卫
function goodExample(value: unknown) {
  if (typeof value === 'string') {
    return value.toUpperCase()
  }
  throw new Error('Expected string')
}

# 21.11. 避免过度使用 any

// 不推荐
let data: any = getData()
let result = (data as any).someProperty

// 推荐:定义具体类型
interface DataType {
  someProperty: string
}
let data: unknown = getData()
let result = (data as DataType).someProperty

# 21.12. 结合类型守卫使用

function isApiResponse(value: unknown): value is ApiResponse {
  return (
    typeof value === 'object' &&
    value !== null &&
    'data' in value &&
    'status' in value
  )
}

function processResponse(response: unknown) {
  if (isApiResponse(response)) {
    // 类型收窄,无需断言
    console.log(response.data)
  }
}

# 21.12.1. 注意事项

  1. 类型断言不会改变运行时值:它只是编译时的类型提示
  2. 可能导致运行时错误:如果断言错误,运行时可能出错
  3. 不是类型转换:不会实际转换值的类型
  4. 在 JSX 中避免尖括号语法:会与 JSX 语法冲突
  5. 优先考虑类型守卫:更安全的方式是运行时类型检查

# 22. 什么是 Mixin?

Mixin 是一种设计模式,允许将多个类的功能组合到一个类中,实现代码复用和组合式继承。在 TypeScript 中,Mixin 通过交叉类型和类表达式来实现。

上次更新: