有没有一种不那么冗长的方法可以接受TypeScript中的一组接口



问题

这是我的问题的一个大大简化的变体:

我有一个看起来像这样的模型:

interface Instrument {
name: string;
// ...more properties that all instruments have in common...
}
interface Guitar extends Instrument {
type: "classical" | "electric";
// ...more properties that only a guitar has...
}
interface Flute extends Instrument {
range: "concert" | "piccolo" | "g-alto" | "g-bass";
// ...properties that only a flute has...
}
interface Artist {
instrument: Guitar | Flute;
}

现在,每次我添加新乐器时,我都必须记住将其添加到艺术家接受的乐器中。我想知道是否有一种方法可以让我定义某种抽象的仪器接口,并告诉Artist.instrument接受每个扩展Instrument的接口。

期望

所以理想情况下,这看起来像这样:

interface Artist {
instrument: // everything that inherits from or extends Instrument
}

我也对其他方法持开放态度。如果有更简单的方法来解决这个问题,请告诉我。

我尝试过的

我已经尝试过简单地接受Instrument作为Artist.instrument的类型,但没有成功。

interface Artist {
instrument: Instrument
}
interface Guitar extends Instrument {
type: "classical" | "electric";
}
const guitar: Guitar = {
name: "Guitar",
type: "electric"
}
const jimiHendrix: Artist = {
instrument: {
name: "Guitar",
type: "electric"
}
}

在这种情况下,我得到了以下错误:

TS2322: Type '{ name: string; type: string; }' is not assignable to type 'Instrument'.   Object literal may only specify known properties, and 'type' does not exist in type 'Instrument'.

我的猜测是,您所做的导致错误的尝试看起来像这样:

interface Instrument {
key: string;
}
interface Guitar extends Instrument {
isElectric?: boolean;
stringCount: number;
}
interface Flute extends Instrument {
bodyType: "wood" | "metal";
}
interface Artist {
instrument: Instrument
}
const artist:Artist = {
instrument: {
key: "A",
stringCount: 12,
},
};

其中stringCount属性产生此错误:

Type '{ key: string; stringCount: number; }' is not assignable to type 'Instrument'.
Object literal may only specify known properties, and 'stringCount' does not exist in type 'Instrument'.

这里的问题是,您可以通过从Instrument继承的接口定义任何可能的属性,而TypeScript无法验证它不知道的内容。

一种解决方案是将仪器定义分解为一个单独的变量,TypeScript可以验证该变量:

const instrument:Guitar = {
key: "A",
stringCount: 12,
};
const artist:Artist = {
instrument,
};

这不会产生错误。TypeScript很高兴,因为instrument变量可以被验证,并且它可以被分配给artistinstrument属性,因为它满足接口。

另一种解决方案是使Artist成为一种通用类型,在这种类型中,您可以更明确地定义艺术家演奏的乐器类型(为方便起见,默认为Instrument):

interface Artist<T extends Instrument=Instrument> {
instrument: T
}
const artist:Artist<Guitar> = {
instrument: {
key: "A",
stringCount: 12,
}
};

我终于能够用一个变通方法解决这个问题。

正如我的问题所示,在艺术家对象中定义乐器是行不通的。如果我在外部定义它,然后将变量传递给艺术家对象,它就会起作用。

interface Instrument {
name: string;
}
interface Guitar extends Instrument {
type: "classical" | "electric";
}
interface Artist {
instrument: Instrument;
}
const guitar: Guitar = {
name: "Guitar",
type: "electric",
};
const jimiHendrix: Artist = {
instrument: guitar,
};

这为我解决了这个问题,但我仍然看不出为什么在Artist.instrument中定义有效的仪器属性会被TS拒绝

最新更新