Home > Software engineering >  How to do this type-safely: "the types 'Vehicle<C>' and 'Vehicle<CarCon
How to do this type-safely: "the types 'Vehicle<C>' and 'Vehicle<CarCon

Time:01-21

This code (which is weird, but I did it only to pose this question):

interface Vehicle<C> {
    id: string
    name: string
    config: C | null
}

interface CarConfig {
    leatherSeats: boolean
}

const Car: Vehicle<CarConfig> = {
    id: "car",
    name: "car",
    config: null
}

interface TruckConfig {
    numberOfWheels: number
}

const Truck: Vehicle<TruckConfig> = {
    id: "truck",
    name: "truck",
    config: null
}

function getVehicleConfig<C>(vehicle: Vehicle<C>): C | null {
    if (vehicle === Car) {
        return vehicle.config
    }
    if (vehicle === Truck) {
        return vehicle.config
    }
    throw new Error("Unknown vehicle")
}

Compiles with this error:

src/explain.ts:28:9 - error TS2367: This condition will always return 'false' since the types 'Vehicle<C>' and 'Vehicle<CarConfig>' have no overlap.

The expectation here is that (due to constraints in another part of the software) vehicle in getVehicleConfig will always be either the Car object or the Truck object, and getVehicleConfig will return the config associated with the instance.

What is the right way to express getVehicleConfig in a type safe manner?

CodePudding user response:

The way to go would be a discriminated union :

type VehiculeType= 'Car' | 'Truck';


interface Vehicle<C> {
    id: string
    name: string
    config: C | null
    type: VehiculeType;
}

interface CarConfig {
    leatherSeats: boolean
}

const Car: Vehicle<CarConfig> = {
    type: 'Car',
    id: "car",
    name: "car",
    config: null
}

interface TruckConfig {
    numberOfWheels: number
}

const Truck: Vehicle<TruckConfig> = {
    type: 'Truck',
    id: "truck",
    name: "truck",
    config: null
}

function getVehicleConfig<C>(vehicle: Vehicle<C>): C | null {
    if (vehicle.type === 'Car') {
        return vehicle.config
    }
    if (vehicle.type === 'Truck') {
        return vehicle.config
    }
    throw new Error("Unknown vehicle") 
}
  •  Tags:  
  • Related