【问题标题】:Inferring properties from extended interface从扩展接口推断属性
【发布时间】:2021-06-01 00:24:06
【问题描述】:

背景

我有以下基本接口:

interface IVehicle {
  readonly id: number;
  readonly price: number;
}

然后是这两个:

interface ICar {
  readonly type: 'land';
  readonly manufacturers: ICarManufacturer[];
}

interface IAirplane {
  readonly type: 'air';
  readonly manufacturers: IAirplaneManufacturer[];
}

车辆只能是汽车或飞机。为了这篇文章,这些都被相当简化了......


我尝试了什么

为了解决这个问题,我首先扩展了每个“子类”,例如:

interface Airplane extends Vehicle {

这很好,但我也希望能够在我的代码中类型不是立即知道的部分中执行以下操作:

const vehicle = getVehicle(id); // Returns a "Vehicle"
if (vehicle.type === 'car') {
  processCarManufacturers(manufacturers); // Fn expects a "Car" - How to infer that type from the "if" above?

我也尝试过使用泛型,但这似乎是错误的:

interface Vehicle<T extends Car | Vehicle> {...}
interface Car extends Vehicle<Car> {...}

但即便如此,推理似乎也不是这样......


我希望能够使用IVehicle,并且通过一些属性能够推断出对象的其余属性——同时,在代码的其他部分,能够使用ICar 及其所有4 个属性。

这似乎是某种循环依赖。不知道我是否会掉进兔子洞……

【问题讨论】:

  • 你有接口的实现,还是只有类型可以使用?
  • @VLAZ,你能详细说明一下吗?没有关注...
  • 是否有实现这些接口的实际类?或者你只是做类似const plane: IAirplane = { type: "air", manufacturers: []} 的事情 - 使用带有普通对象的类型?
  • @VLAZ,啊,就是后者。
  • 完美,谢谢。答案可能取决于它,而我想到的更简单的方法最适合仅使用类型。给我几分钟。

标签: typescript


【解决方案1】:

您可以使您的Vehicle 类成为所有可能车辆的简单联合。这可以像这样工作:

interface IVehicle {
  readonly id: number;
  readonly price: number;
}

interface ICar extends IVehicle{
  readonly type: 'land';
  readonly manufacturers: ICarManufacturer[];
}

interface IAirplane extends IVehicle {
  readonly type: 'air';
  readonly manufacturers: IAirplaneManufacturer[];
}

type Vehicle = 
  | ICar
  | IAirplane

IVehicle 是基础,ICarIAirplane 都扩展了它。这样可以确保它们各自具有相同的共享属性 idprice

Vehicle 类型是 ICarIAirplane 的联合。这里重要的是它们都共享type 属性。这允许您将其用作discriminated union。当你这样做时

if (vehicle.type === 'land') {
  // ...
}

那么在if 内,车辆只能是汽车,因为它是唯一一个将type 设置为文字"land" 的联合成员。

因此,如果您的代码如下所示,编译器将接受:

interface ICarManufacturer { tyres: boolean; }

interface IAirplaneManufacturer { wings: boolean; }

interface IVehicle {
  readonly id: number;
  readonly price: number;
}

interface ICar extends IVehicle{
  readonly type: 'land';
  readonly manufacturers: ICarManufacturer[];
}

interface IAirplane extends IVehicle {
  readonly type: 'air';
  readonly manufacturers: IAirplaneManufacturer[];
}

type Vehicle = 
  | ICar
  | IAirplane


const vehicle = getVehicle(id); // Returns a "Vehicle"
if (vehicle.type === 'land') {
  processCarManufacturers(vehicle.manufacturers);
}

Playground Link

【讨论】:

  • 谢谢!我希望有一种方法可以只使用接口,但这也很好。干杯。
猜你喜欢
  • 2021-02-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-03-31
  • 2016-08-10
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多