I currently have the following interface:
export interface Vehicle {
id: number;
type: 'car' | 'airplane';
jet_engines?: number;
wheels?: number;
}
But I also don't want cars to accept the jet_engines
property, and airplanes shouldn't have the wheels
property.
I want to use it like this:
const func = (vehicle: Vehicle) => {
if (vehicle.type === 'airplane') {
// TS should know here that `jet_engines` exists in `vehicle`.
}
}
I want to avoid using like this:
const func = (vehicle: Car | Airplane) => {
Is this possible? Is this possible while keeping Vehicle
an interface (not changing it to a type)?
CodePudding user response:
Yes you can:
export type Vehicle = {
id: number
} & VehicleType
type VehicleType = Car | Airplane
type Car = {
type: 'car'
wheels: number
}
type Airplane = {
type: 'airplane'
jet_engines: number
}
const func = (vehicle: Vehicle) => {
if (vehicle.type === 'airplane') {
// TS should know here that `jet_engines` exists in `vehicle`.
}
}
CodePudding user response:
You can update Vehicle to the following:
export type Vehicle =
| {
id: number;
type: 'car';
wheels: number;
}
| {
id: number;
type: 'airplane';
jet_engines: number;
}
This way, you can do something like:
if (vehicle.type === 'car') {
// TS knows the vehicle has wheels
} else {
// TS knows the vehicle has jet engines
}
And, if you have multiple common fields, to avoid duplication, you can do something like the following:
type VehicleCommonFields = {
id: number;
// other common fields
}
type VehicleTypes =
| {
type: 'car';
wheels: number;
}
| {
type: 'airplane';
jet_engines: number;
}
export type Vehicle = VehicleCommonFields & VehicleTypes
CodePudding user response:
You can create a VehicleType
that lists all possible vehicles.
Then create a generic type that holds the common fields.
Then create interfaces for both (Car/Airplay) and in the end create a union type.
You can even create a type guard function. Something similar to this:
type VehicleType = 'car' | 'airplane';
export interface GenericVehicle <T extends VehicleType> {
id: number;
type: T;
}
interface Airplane extends GenericVehicle<'airplane'> {
jet_engines: number
}
interface Car extends GenericVehicle<'car'> {
wheels: number
}
type Vehicle = Airplane | Car
const isCar = (vehicle: Vehicle): vehicle is Car => {
return vehicle.type === 'car';
}
function doSmth(vehicle: Vehicle) {
if (isCar(vehicle)) {
vehicle.wheels;
} else {
vehicle.jet_engines;
}
}