假设已经定义了以下类型:
interface Shape {
color: string;
}
现在,考虑以下几种方法来向这种类型添加额外的属性:
扩展
interface Square extends Shape {
sideLength: number;
}
交集
type Square = Shape & {
sideLength: number;
}
这两种方法的区别是什么?
而且,为了完整起见和出于好奇,还有其他能够产生类似结果的方式吗?
假设已经定义了以下类型:
interface Shape {
color: string;
}
现在,考虑以下几种方法来向这种类型添加额外的属性:
扩展
interface Square extends Shape {
sideLength: number;
}
交集
type Square = Shape & {
sideLength: number;
}
这两种方法的区别是什么?
而且,为了完整起见和出于好奇,还有其他能够产生类似结果的方式吗?
是的,有一些差异可能与您的情况相关或不相关。
其中最显著的可能是当两种类型中都存在具有相同属性键的成员时,它们如何处理的差异。
请考虑:
interface NumberToStringConverter {
convert: (value: number) => string;
}
interface BidirectionalStringNumberConverter extends NumberToStringConverter {
convert: (value: string) => number;
}
extends
导致错误,因为派生接口声明了一个与派生接口中具有相同键但具有不兼容签名的属性。error TS2430: Interface 'BidirectionalStringNumberConverter' incorrectly extends interface 'NumberToStringConverter'.
Types of property 'convert' are incompatible.
Type '(value: string) => number' is not assignable to type '(value: number) => string'.
Types of parameters 'value' and 'value' are incompatible.
Type 'number' is not assignable to type 'string'.
type NumberToStringConverter = {
convert: (value: number) => string;
}
type BidirectionalStringNumberConverter = NumberToStringConverter & {
convert: (value: string) => number;
}
// And this is a good thing indeed as a value conforming to the type is easily conceived
const converter: BidirectionalStringNumberConverter = {
convert: (value: string | number) => {
return (typeof value === 'string' ? Number(value) : String(value)) as string & number; // type assertion is an unfortunately necessary hack.
}
}
const s: string = converter.convert(0); // `convert`'s call signature comes from `NumberToStringConverter`
const n: number = converter.convert('a'); // `convert`'s call signature comes from `BidirectionalStringNumberConverter`
这导致了另一个有趣的差异,接口声明是开放式的。新成员可以在任何地方添加,因为在同一声明空间中具有相同名称的多个接口声明会被合并。lib.d.ts
interface Array<T> {
// map, filter, etc.
}
array-flat-map-polyfill.ts
interface Array<T> {
flatMap<R>(f: (x: T) => R[]): R[];
}
if (typeof Array.prototype.flatMap !== 'function') {
Array.prototype.flatMap = function (f) {
// Implementation simplified for exposition.
return this.map(f).reduce((xs, ys) => [...xs, ...ys], []);
}
}
extends
子句,尽管在不同的文件中指定了接口,但这些接口都在全局范围内,并按名称合并为一个单一的逻辑接口声明,其中包含两组成员。(对于模块作用域的声明,可以使用稍微不同的语法来完成相同的操作)type
声明中的交叉类型是封闭的,不会被合并。
type NewInterface = FirstInterface & SecondInterface;
。 - Zach Saucier