一、&
的基本用法
交叉类型的概念非常直观:T & U
意味着一个值既是类型 T
,又是类型 U
。对于对象类型来说,就是将它们的属性合并。
来看一个最简单的例子:
typescript
interface HasName {
name: string;
}
interface HasAge {
age: number;
}
// 使用交叉类型 & 创建一个新类型 Person
type Person = HasName & HasAge;
const person: Person = {
name: 'Alice',
age: 30
}; // OK, person 对象同时拥有 name 和 age 属性
Person
类型现在要求它的实例必须同时满足 HasName
和 HasAge
的所有要求。&
在这里就像胶水一样,把两个接口"粘"在了一起。
这不仅仅限于两个类型,你可以随心所欲地"粘"更多:
typescript
interface HasId {
id: number;
}
type UserProfile = HasName & HasAge & HasId;
const user: UserProfile = {
id: 1,
name: 'Bob',
age: 25
};
二、深入理解 &
的AND逻辑
2.1 原始类型的交叉 -> never
一个值不可能同时是 string
和 number
。因此,它们的交叉类型就是 never
------一个永远不可能存在值的类型。
typescript
type Impossible = string & number; // 类型推断为 never
let impossibleValue: Impossible;
impossibleValue = "hello"; // Error: 不能将类型""hello""分配给类型"never"
2.2 同名属性的交叉
-
如果同名属性的类型相同,则合并后类型不变。
typescriptinterface A { prop: string; } interface B { prop: string; } type C = A & B; // C 的类型是 { prop: string; }
-
如果同名属性的类型不同 ,则合并后的属性类型会是这两个类型的交叉类型。
typescriptinterface X { id: string; } interface Y { id: number; } type Z = X & Y; // Z 的类型是 { id: string & number; } // 因为 string & number 是 never,所以 id 的类型是 never const z: Z = { id: (():never=>{ throw new Error('')})()/* 只能给id赋值never */ };
这意味着,
Z
类型实际上是一个无法被创建的类型,因为你无法为id
属性提供一个既是string
又是number
的值。
2.3 组合优于继承
交叉类型是实践"组合优于继承"设计原则的利器。把复杂的多重继承简化为创建一个个可复用的小类型,然后按需将它们组合起来。
typescript
interface Loggable {
log(message: string): void;
}
interface Serializable {
serialize(): string;
}
// 我们可以轻松创建一个既能打日志又能序列化的对象类型
type LoggableAndSerializable = Loggable & Serializable;
class MyObject implements LoggableAndSerializable {
log(message: string) {
console.log(message);
}
serialize() {
return JSON.stringify(this);
}
}
let obj = new MyObject();
obj.log('hello'); // 输出: hello
console.log(obj.serialize()); // 输出: {}
总结
如果你喜欢本教程,记得点赞+收藏!关注我获取更多JavaScript/TypeScript开发干货