交叉类型(Intersection Types)是将多个类型叠加合并组成新的类型,新类型包含了所有被合并类型的所有属性。
交叉类型的使用场景:Mixins、不适合用类来定义的场景
例如,下述代码显示了交叉类型 Foo & Bar
同时具备 Foo
和 Bar
两种类型的成员,就是说这个类型的对象同时拥有了这两种类型的成员。
interface Foo {name: string;age: number;sayName: (name: string) => void;}interface Bar {name: string;gender: number;sayGender: (gender: string) => void;}let Tom: Foo & Bar;console.log(Tom.age);console.log(Tom.gender);
较多在混入(Mixins)或其他不适合典型面向对象模型的地方看到交叉类型的使用。
🌰 示例:下面代码(官方示例)展示了如何创建一个混入的简单例子
function extend<T, U>(first: T, second: U): T & U {let result = <T & U>{};for (let prop in first) {(<any>result)[prop] = (<any>first)[prop];}for (let prop in second) {if (!result.hasOwnProperty(id)) {(<any>result)[prop] = (<any>second)[prop];}}return result;}class Person {constructor(public name: string) {}}interface Loggable {log(): void;}class ConsoleLogger implements Loggable {log() {// ...}}const tom = extend(new Person('Jim'), new ConsoleLogger());const name = tom.name;tom.log();
上述代码相对比较简单,但可能对下面这段代码会产生疑问:
(<any>result)[prop] = first[prop];
既然 result
已经被断言为交叉类型,那么 first
所具有的属性 result
都是具有的。
为什么还要将 result
断言为 any
类型呢,因为交叉类型可能导致类型冲突。
🌰 示例:
interface Foo {x: boolean;y: string;}interface Bar {x: boolean;y: number;}type Baz = Foo & Bar;type X = Baz['x']; // booleantype Y = Baz['y']; // string & numberdeclare const baz: Baz;declare const foo: Foo;baz['y'] = foo['y'];
上面的代码中,Foo
和 Bar
接口中,y
属性类型冲突,那么最终 y
属性类型为交叉类型 string & number
,同时 string
类型或者 number
类型无法赋值给交叉类型 string & number
,最终导致报错。为了解决这个问题,可以将 result
再断言为 any
类型,这样 result
所有属性均为 any
类型,那么赋值就不会报错了。