08. TypeScript进阶:接口、泛型与类型体操

TypeScript 进阶:接口、泛型与类型体操

TypeScript Advanced

上一期我们学习了 TypeScript 的基础类型。这一期,我们深入接口、泛型和高级类型系统——这些是 TypeScript 真正强大的地方。

接口(Interface)

接口定义了对象的形状。

基本用法

interface User {
  id: number
  name: string
  email: string
}

function printUser(user: User) {
  console.log(`${user.name} `)
}

printUser({
  id: 1,
  name: "Alice",
  email: "alice@example.com"
})

可选属性

interface User {
  id: number
  name: string
  email?: string  // 可选
}

let user1: User = { id: 1, name: "Alice" }
let user2: User = { id: 2, name: "Bob", email: "bob@example.com" }

只读属性

interface User {
  readonly id: number
  name: string
}

let user: User = { id: 1, name: "Alice" }
user.id = 2  // Error: Cannot assign to 'id' because it is read-only

函数类型

interface SearchFunc {
  (source: string, subString: string): boolean
}

let mySearch: SearchFunc = (source, sub) => {
  return source.search(sub) !== -1
}

可索引类型

interface StringArray {
  [index: number]: string
}

let myArray: StringArray = ["Bob", "Fred"]
let myStr: string = myArray[0]

类类型

interface ClockInterface {
  currentTime: Date
  setTime(d: Date): void
}

class Clock implements ClockInterface {
  currentTime: Date = new Date()
  
  setTime(d: Date) {
    this.currentTime = d
  }
  
  constructor(h: number, m: number) {}
}

继承接口

interface Shape {
  color: string
}

interface Square extends Shape {
  sideLength: number
}

let square: Square = {
  color: "blue",
  sideLength: 10
}

多继承:

interface Shape {
  color: string
}

interface PenStroke {
  penWidth: number
}

interface Square extends Shape, PenStroke {
  sideLength: number
}

泛型(Generics)

泛型让我们可以编写可重用的组件。

基本泛型

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

let output1 = identity("myString")  // 类型为 string
let output2 = identity(123)  // 类型推断为 number

泛型变量

function loggingIdentity(arg: T[]): T[] {
  console.log(arg.length)
  return arg
}

泛型类型

let myIdentity: (arg: T) => T = identity

// 对象字面量形式
let myIdentity2: { (arg: T): T } = identity

泛型接口

interface GenericIdentityFn {
  (arg: T): T
}

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

let myIdentity: GenericIdentityFn = identity

把泛型参数移到接口:

interface GenericIdentityFn {
  (arg: T): T
}

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

let myIdentity: GenericIdentityFn = identity

泛型类

class GenericNumber {
  zeroValue: T
  add: (x: T, y: T) => T
}

let myGenericNumber = new GenericNumber()
myGenericNumber.zeroValue = 0
myGenericNumber.add = (x, y) => x + y

泛型约束

interface Lengthwise {
  length: number
}

function loggingIdentity(arg: T): T {
  console.log(arg.length)  // 现在可以访问 .length
  return arg
}

loggingIdentity("hello")  // OK
loggingIdentity([1, 2, 3])  // OK
loggingIdentity(123)  // Error

在泛型中使用类类型

function create(c: { new(): T }): T {
  return new c()
}

class BeeKeeper {
  hasMask: boolean = true
}

class ZooKeeper {
  nametag: string = "Mikle"
}

class Animal {
  numLegs: number = 4
}

class Bee extends Animal {
  keeper: BeeKeeper = new BeeKeeper()
}

class Lion extends Animal {
  keeper: ZooKeeper = new ZooKeeper()
}

function createInstance<A>(c: new () => A): A {
  return new c()
}

createInstance(Lion).keeper.nametag
createInstance(Bee).keeper.hasMask

高级类型

交叉类型

将多个类型合并为一个。

interface Person {
  name: string
}

interface Loggable {
  log(): void
}

type PersonLoggable = Person & Loggable

const person: PersonLoggable = {
  name: "Alice",
  log() {
    console.log(this.name)
  }
}

联合类型

type StringOrNumber = string | number

function add(a: StringOrNumber, b: StringOrNumber) {
  if (typeof a === "string" || typeof b === "string") {
    return a.toString() + b.toString()
  }
  return a + b
}

类型保护

interface Fish {
  swim(): void
}

interface Bird {
  fly(): void
}

function isFish(pet: Fish | Bird): pet is Fish {
  return (pet as Fish).swim !== undefined
}

function move(pet: Fish | Bird) {
  if (isFish(pet)) {
    pet.swim()
  } else {
    pet.fly()
  }
}

typeof 类型保护

function padLeft(value: string, padding: string | number) {
  if (typeof padding === "number") {
    return Array(padding + 1).join(" ") + value
  }
  if (typeof padding === "string") {
    return padding + value
  }
  throw new Error(`Expected string or number, got '${padding}'.`)
}

instanceof 类型保护

class Dog {
  bark() {}
}

class Cat {
  meow() {}
}

function speak(pet: Dog | Cat) {
  if (pet instanceof Dog) {
    pet.bark()
  } else {
    pet.meow()
  }
}

null 检查

function f(str: string | null) {
  if (str === null) {
    return "default"
  }
  return str.toUpperCase()
}

可选链:

let x = foo?.bar.baz()
// 等价于
let x = foo === null || foo === undefined ? undefined : foo.bar.baz()

空值合并:

let x = foo ?? "default"
// 等价于
let x = foo !== null && foo !== undefined ? foo : "default"

类型推断

基础推断

let x = 3  // 推断为 number
let y = [0, 1, null]  // 推断为 (number | null)[]

上下文推断

window.onmousedown = function(mouseEvent) {
  console.log(mouseEvent.button)  // 推断为 MouseEvent
}

类型推断方向

// 从右向左
let x = 3

// 从左向右(上下文类型)
window.onmousedown = function(mouseEvent) {
  return mouseEvent.button
}

映射类型

从旧类型创建新类型。

Partial

所有属性变为可选。

type Partial = {
  [P in keyof T]?: T[P]
}

interface User {
  id: number
  name: string
}

type PartialUser = Partial
// { id?: number; name?: string; }

Required

所有属性变为必选。

type Required = {
  [P in keyof T]-?: T[P]
}

Readonly

所有属性变为只读。

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

Pick

选取部分属性。

type Pick = {
  [P in K]: T[P]
}

interface User {
  id: number
  name: string
  email: string
}

type UserPreview = Pick
// { id: number; name: string; }

Omit

排除部分属性。

type Omit = Pick<T, Exclude>

type UserWithoutEmail = Omit
// { id: number; name: string; }

Record

创建对象类型。

type Record = {
  [P in K]: T
}

type UserMap = Record

条件类型

根据条件选择类型。

type ReturnType = T extends (...args: any[]) => infer R ? R : any

function foo(): number {
  return 1
}

type R = ReturnType  // number

实战示例

API 响应类型

interface ApiResponse {
  code: number
  message: string
  data: T
}

interface User {
  id: number
  name: string
}

async function fetchUser(id: number): Promise<ApiResponse> {
  const response = await fetch(`/api/users/${id}`)
  return response.json()
}

// 使用
const result = await fetchUser(1)
console.log(result.data.name)

事件处理

interface EventHandler {
  (event: T): void
}

interface ClickEvent {
  type: "click"
  x: number
  y: number
}

const handler: EventHandler = (event) => {
  console.log(`Clicked at ${event.x}, ${event.y}`)
}

状态管理

type State = {
  user: User | null
  loading: boolean
  error: string | null
}

type Action =
  | { type: "FETCH_START" }
  | { type: "FETCH_SUCCESS"; payload: User }
  | { type: "FETCH_ERROR"; payload: string }

function reducer(state: State, action: Action): State {
  switch (action.type) {
    case "FETCH_START":
      return { ...state, loading: true }
    case "FETCH_SUCCESS":
      return { ...state, loading: false, user: action.payload }
    case "FETCH_ERROR":
      return { ...state, loading: false, error: action.payload }
  }
}

总结

TypeScript 的高级特性:

  • 接口:定义对象形状
  • 泛型:创建可重用组件
  • 类型保护:缩小类型范围
  • 映射类型:从旧类型创建新类型
  • 条件类型:根据条件选择类型

下一期,我们学习 TypeScript 的项目配置和最佳实践。


进阶资源

Views: 6