Oasis's Cloud

一个人的首要责任,就是要有雄心。雄心是一种高尚的激情,它可以采取多种合理的形式。
—— 《一个数学家的辩白》

复合类型

作者:oasis


类型的组合

元组类型的形式如同数组:

type Point  = [number, number]
元组类型提供了一种特殊的分组数据的方式,允许我们将不同类型的多个值作为一个值传递。在 Point 的例子中,将两个 number 值作为一个元组传递。上述的 Point 类型表示二维坐标系中的 x 和 y,我们也可通过元组表示三维空间中的点:

type Point = [number, number, number]

除了表示三维空间中的点,也可以用来表示矩阵或张量等:

// 2x2 矩阵(二阶张量)
type Matrix2x2 = [[number, number], [number, number]]

// 或者更通用的矩阵类型
type Matrix = number[][]

通过类和泛型可以实现特定的元组:

class Pair<T1, T2> {
    x: T1
    y: T2
    constructor(x: T1, y: T2) {
        this.x = x
        this.y = y
    }
}
type Point = Pair<number, number>

// 或
class Pair<T1, T2> {
    constructor(public x: T1, public y: T2) {}
}

class Point extends Pair<number, number> {
    constructor(x: number, y: number) {
        super(x, y)
    }
}

// 或
interface Pair<T1, T2> {
    x: T1
    y: T2
}
type Point = Pair<number, number>

使用 interface 的实现更轻量,适合仅定义类型的场景。

注意

class Pair<T1, T2> {
    constructor(public x: T1, public y: T2) {}
}
等价于
class Pair<T1, T2> {
    x: T1
    y: T2
    constructor(x: T1, y: T2) {
        this.x = x
        this.y = y
    }
}

编译后的对照

执行顺序: 1. new Point(1, 2) 被调用 2. 进入 Point 的构造函数 3. 调用 super(1, 2),执行父类 Pair 的构造函数 4. 父类构造函数中,public x: T1 和 public y: T2 会自动执行 this.x = 1 和 this.y = 2 5. 返回后,Point 实例的 x 和 y 已被设置

类型组合成多选一类型

通过类型表达多选一,可以通过枚举实现:

enum DayOfWeek {
    Sunday,
    Monday,
    Tuesday,
    Wednesday,
    Thursday,
    Friday,
    Saturday
}

上述枚举作为参数时很好用,当函数处理过后,返回值可能是枚举或 undefined,这时可以通过 | 实现这种行为:

function getDayOfWeek(input: string): DayOfWeek | undefined {}

代数数据类型

代数数据类型(ADT)是类型系统中组合类型的方式。它包括两种组合方式:乘积类型与和类型。

乘积类型(Product Type)

乘积类型表示”同时拥有”多个类型的值,例如元组类型 [Two, Three] 表示同时拥有 Two 类型和 Three 类型的值。

例如给定 enum Two {A, B}enum Three {C, D, E},元组类型 [Two, Three] 这一类型就是乘积类型。

计算方式:乘积类型的可能值数量 = 第一个类型的可能值数量 × 第二个类型的可能值数量

和类型(Sum Type)

和类型表示”或者”的关系,例如联合类型 Two | Three 表示值可以是 Two 类型或者 Three 类型。

给定 enum Two {A, B}enum Three {C, D, E},类型 Two | Three 这一类型是和类型。

计算方式:和类型的可能值数量 = 第一个类型的可能值数量 + 第二个类型的可能值数量

总结

注意⚠️:代数数据类型所要传达的含义是每种类型可能的值有多少种情况,也就是可能的集合是什么样的。

| 表示联合类型,等价于代数类型中的和类型。& 是交集类型,不是代数类型中的乘积类型,元组是代数类型中的乘积类型。

TypeScript 使用的是结构类型系统(Structural Typing),而不是名义类型系统(Nominal Typing)。这使得 & 的行为与数学中的集合交集不完全相同。

数学视角:A ∩ B 要求元素”同时属于A和B”,TypeScript视角:A & B 要求元素”同时满足A和B的结构约束”。

& 更像是约束合并而非交集

函数类型的特殊行为:

type F1 = (x: number) => number;
type F2 = (x: string) => string;

// TypeScript:F1 & F2 = 函数重载
type F = F1 & F2;

// 实际上这是可能的:
const f: F = <T extends unknown>(x: T): T => {
    if (typeof x === "number") return (x * 2) as T;
    if (typeof x === "string") return x.toUpperCase() as T;
    throw new Error("Unsupported type");
} as F;