类型运算:组合与变换

目录

第 1 节 概述

上一篇我们认识了基础类型:stringnumberboolean……

但现实中的数据往往是"或"、“且"的关系——“这个值可以是字符串或数字”、“这个人必须有姓名和年龄”。

TypeScript 的类型系统支持类型运算。就像数字能做加减乘除一样,类型之间也可以组合和变换。

第 2 节 联合类型

联合类型用 | 表示"或”。一个值可以是几种类型中的任意一种:

1
2
3
4
5
let id: string | number

id = "abc-123"   // ✅
id = 42          // ✅
id = true        // ❌ boolean 不在联合中

最常见的用法是搭配字面量类型,限定一组可选值:

1
2
3
4
5
6
7
8
type Direction = "up" | "down" | "left" | "right"

function move(direction: Direction) {
  // direction 只能是这四个值之一
}

move("up")    // ✅
move("back")  // ❌ 不在允许范围内

函数的可选参数也可以用到联合类型。比如一个参数既可以是 string,也可以是 string[]

1
2
3
4
5
6
function format(input: string | string[]) {
  if (Array.isArray(input)) {
    return input.join(", ")
  }
  return input
}

第 3 节 交叉类型

交叉类型用 & 表示"且"。一个值同时满足所有条件:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
type HasName = { name: string }
type HasAge = { age: number }

type Person = HasName & HasAge

const person: Person = {
  name: "张三",
  age: 18,
}
// 必须同时有 name 和 age
注意

如果交叉的类型有同名但类型不同的属性,会产生 never

1
2
3
type A = { x: string }
type B = { x: number }
type C = A & B  // x 的类型是 string & number → never

第 4 节 type 别名

type 可以为任意类型起一个名字。它不创建新类型,只是给现有类型一个别名:

1
2
3
4
5
type Name = string
type Age = number

const myName: Name = "张三"
const myAge: Age = 18

它的真正价值在于给复杂类型一个有意义的名字:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
type Point = {
  x: number
  y: number
}

type Status = "idle" | "loading" | "success" | "error"

type ApiResponse<T> = {
  data: T
  status: Status
}

type 定义的类型,可以在任何地方引用,避免重复书写。

第 5 节 keyof 操作符

keyof 取出一个对象类型的所有键,组成联合类型:

1
2
3
4
5
6
7
8
type Person = {
  name: string
  age: number
  email: string
}

type PersonKeys = keyof Person
// 等价于 "name" | "age" | "email"

实际用途:限制函数参数只能是对象的某个键:

1
2
3
4
5
6
7
function getValue<T>(obj: T, key: keyof T) {
  return obj[key]
}

const person = { name: "张三", age: 18 }
getValue(person, "name")   // ✅
getValue(person, "gender") // ❌ "gender" 不在 keyof 中

第 6 节 typeof 操作符

typeof 在类型上下文中,获取一个值的 TypeScript 类型:

1
2
3
4
5
6
7
const person = {
  name: "张三",
  age: 18,
}

type PersonType = typeof person
// 等价于 { name: string; age: number }

这在处理已有数据时非常有用,不用手动再写一遍类型:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
const config = {
  url: "https://api.example.com",
  timeout: 5000,
  retry: true,
}

type Config = typeof config
// { url: string; timeout: number; retry: boolean }

function fetchData(config: Config) {
  // ...
}
技巧

typeofkeyof 经常搭配使用:keyof typeof config 可以拿到配置对象的所有键。

第 7 节 索引访问类型

你可以像访问对象属性那样,访问类型的某个属性的类型:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
type Person = {
  name: string
  age: number
  address: {
    city: string
    street: string
  }
}

type NameType = Person["name"]
// string

type AddressType = Person["address"]
// { city: string; street: string }

type NameOrAge = Person["name" | "age"]
// string | number

甚至可以传入一个类型作为索引:

1
2
3
type PersonKeys = keyof Person  // "name" | "age" | "address"
type Values = Person[keyof Person]
// string | number | { city: string; street: string }

这一篇我们学会了组合类型。就像是学会了加法和乘法——你能用基础类型的砖块,搭出更复杂的结构。

有了这些工具,我们可以描述更真实的数据了。 下一篇看看数组和元组——如何给"一组数据"加上类型。

目录