VVLL.net

Type Compatibility(类型兼容性)

日期:2024-08-22 09:58:31

TypeScript 类型兼容性是指判断一个类型是否可以被另一个类型替代,换句话说,就是一个类型的值是否可以赋值给另一个类型的变量。TypeScript 使用结构化类型系统(也称为鸭子类型或子结构类型),基于成员的实际结构而不是名称来判断类型兼容性。

1. 基本规则

在 TypeScript 中,一个类型 S 可以赋值给另一个类型 T,当且仅当 T 兼容 S。类型兼容性主要取决于两个方面:

  • 结构兼容性:即一个类型的属性集合是否是另一个类型属性集合的子集。
  • 可分配性:即一个类型的值是否可以赋值给另一个类型。

2. 接口兼容性

当判断两个接口类型的兼容性时,TypeScript 会检查一个接口的属性是否可以包含在另一个接口中。

interface Person {
  name: string;
  age: number;
}

interface Employee {
  name: string;
  age: number;
  position: string;
}

let p: Person;
let e: Employee = { name: "John", age: 30, position: "Developer" };
p = e; // OK
// e = p; // Error: Property 'position' is missing in type 'Person' but required in type 'Employee'

在这个例子中,Employee 包含 Person 所有的属性,因此 Employee 类型可以赋值给 Person 类型。

3. 类兼容性

类的兼容性与接口类似,但还会考虑到类的静态成员和构造函数。只考虑实例成员和方法。

class Animal {
  name: string;
}

class Dog extends Animal {
  breed: string;
}

let a: Animal;
let d: Dog = { name: "Buddy", breed: "Golden Retriever" };
a = d; // OK
// d = a; // Error: Property 'breed' is missing in type 'Animal' but required in type 'Dog'

4. 函数兼容性

函数的兼容性检查主要涉及参数和返回值的类型。在检查函数兼容性时,TypeScript 会执行双向检查。

  • 参数:参数类型必须兼容(逆变)。
  • 返回值:返回值类型必须兼容(协变)。
let x = (a: number) => 0;
let y = (b: number, s: string) => 0;

y = x; // OK
// x = y; // Error: Cannot assign 'y' to 'x' because 'y' has more parameters than 'x'

let a = () => ({ name: "Alice" });
let b = () => ({ name: "Alice", location: "Seattle" });

a = b; // OK
// b = a; // Error: 'name' is missing in type returned by 'a' but required in type returned by 'b'

5. 泛型兼容性

泛型类型的兼容性与普通类型相似,TypeScript 会检查实际使用时泛型参数的兼容性。

interface Empty<T> {
}

let x: Empty<number>;
let y: Empty<string>;

x = y; // OK, 因为 Empty<T> 结构相同

interface NotEmpty<T> {
  data: T;
}

let a: NotEmpty<number>;
let b: NotEmpty<string>;

// a = b; // Error: Property 'data' is incompatible

6. 枚举兼容性

不同枚举类型之间不兼容,但可以将枚举类型与数字类型相互赋值。

enum Status {
  Ready,
  Waiting
}

enum Color {
  Red,
  Blue,
  Green
}

let s: Status = Status.Ready;
s = 1; // OK

// let c: Color = Status.Ready; // Error: Type 'Status' is not assignable to type 'Color'

7. 高级类型兼容性

联合类型和交叉类型

  • 联合类型(Union Types):A | B 表示 A 或 B 的类型,可以兼容 A 或 B 的类型。
  • 交叉类型(Intersection Types):A & B 表示同时是 A 和 B 的类型。
interface Bird {
  fly(): void;
  layEggs(): void;
}

interface Fish {
  swim(): void;
  layEggs(): void;
}

type Pet = Bird | Fish;

let pet: Pet;
let bird: Bird = { fly: () => {}, layEggs: () => {} };
let fish: Fish = { swim: () => {}, layEggs: () => {} };

pet = bird; // OK
pet = fish; // OK

type AquaticPet = Bird & Fish;

// let aquaticPet: AquaticPet = { fly: () => {}, swim: () => {}, layEggs: () => {} }; // OK
// let birdOnly: AquaticPet = { fly: () => {}, layEggs: () => {} }; // Error: Property 'swim' is missing
// let fishOnly: AquaticPet = { swim: () => {}, layEggs: () => {} }; // Error: Property 'fly' is missing

8. TypeScript 中类型兼容性总结

  • 结构化类型系统:基于成员结构判断类型兼容性。
  • 接口和类:成员类型兼容即可赋值,类需注意静态成员和构造函数。
  • 函数:参数类型逆变,返回值类型协变。
  • 泛型:实际使用时泛型参数需兼容。
  • 枚举:与数字类型互相兼容,不同枚举类型不兼容。
  • 联合类型和交叉类型:分别表示可选类型和组合类型。

通过理解和掌握 TypeScript 的类型兼容性机制,可以编写出更安全、可维护的代码,同时也可以更灵活地利用类型系统的强大功能。

参考

标签