[英]Narrow down type of field, based on another field
我有这个界面:
enum CatEngine {
Electric = "ev",
Petrol = "petrol",
}
interface ElectricEngineDetails {
range: number
}
interface PetrolEngineDetails {
mpg: number
}
interface ICar {
engine: CarEngine;
details: ElectricEngineDetails | PetrolEngineDetails;
}
有没有办法编写一个接受engine
和details
的类型保护,并返回details
,缩小到相应的engine
类型? 我知道它可以就地完成,但我希望将其提取到单独的 function 中。
您可以使用type
而不是ICar
的interface
(或Car
,因为它不再是接口):
type Car = { engine: CarEngine.Electric; details: ElectricEngineDetails } | { engine: CarEngine.Petrol; details: PetrolEngineDetails };
或者您可以为以下类型创建接口:
type Car = IElectricCar | IPetrolCar;
interface IElectricCar {
engine: CarEngine.Electric;
details: ElectricEngineDetails;
}
interface IPetrolCar {
engine: CarEngine.Petrol;
details: PetrolEngineDetails;
}
您可以声明一个通用接口,如下所示:
type ICar<T extends CarEngine> = {
engine: T;
details: T extends CarEngine.Electric ? ElectricEngineDetails : PetrolEngineDetails;
}
这将根据engine
的类型计算details
的类型。 你可以像这样使用它:
const ev: ICar<CarEngine.Electric> = {
engine: CarEngine.Electric,
details: {
range: 23,
}
}
尝试使用mpg
会导致错误,因为 TS 知道details
的类型是ElectricEngineDetails
。
有了这个,你可以编写一个类型保护 function 来缩小类型:
const isElectric = (car: ICar<CarEngine.Electric> | ICar<CarEngine.Petrol>): car is ICar<CarEngine.Electric> => {
if (car.engine === CarEngine.Electric) {
return true;
}
return false;
}
现在你可以像这样使用它:
function something (car: ICar<CarEngine.Petrol> | ICar<CarEngine.Electric>): void {
if (isElectric(car)) {
car.details.range;
} else {
car.details.mpg;
}
}
这是Playground上的完整示例。
你正在寻找的是一个有歧视的工会
基本上,您需要定义一个由所有可能性组成的联合,但您的所有接口都必须具有一个共同的判别属性,这将允许 Typescript 知道如何有条件地消除类型
你可以在这里测试
enum CarEngine {
Electric = "ev",
Petrol = "petrol",
}
interface ElectricEngineDetails {
range: number
}
interface PetrolEngineDetails {
mpg: number
}
// Discriminant union, engine will be the discriminant property
type Car = IElectricCar | IPetrolCar;
interface IElectricCar {
// Electric car will have 'ev' as a type for engine
engine: CarEngine.Electric;
details: ElectricEngineDetails;
}
interface IPetrolCar {
// Petrol car will have 'petrol' as a type for engine
engine: CarEngine.Petrol;
details: PetrolEngineDetails;
}
declare const car: Car
// car types are discriminated based on the `engine` property
if (car.engine === CarEngine.Electric) {
car.details.range // ok
car.details.mpg // Doesn't exist
} else {
// Removed IElectricCar from the union because Typescript know car.engine can
// not be CarEngine.Electric anymore, so only IPetrolCar is available in the union
car.details.mpg // ok
car.details.range // Doesn't exist
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.