Looking at two interfaces, one with a nullable vin
and the other without:
interface IVehicle {
vin: string | null;
model: string;
}
interface IVehicleNonNullVin {
vin: string;
model: string;
}
The goal is to convert a model from IVehicle
to IVehicleNonNullVin
when it's confirmed that vin
is not null
.
Take this scenario for example:
const myVehicle: IVehicle = {
vin: 'abc123',
model: 'm3'
};
const needsVin = (_: IVehicleNonNullVin) => false;
if (myVehicle.vin === null) {
throw new Error('null');
} else {
needsVin(myVehicle);
// ~~~~~~~~~~~~~~~~~~~ 'IVehicle' is not assignable to 'IVehicleNonNullVin'
}
This results in the following error message:
Argument of type 'IVehicle' is not assignable to parameter of type 'IVehicleNonNullVin'.
Types of property 'vin' are incompatible.
Type 'string | null' is not assignable to type 'string'.
Type 'null' is not assignable to type 'string'.
Even though it's certain that the property is not nullable in this case.
Q: How can TypeScript be convinced that the existing model conforms to the type based on type checks within the code flow?
Demo in TS Playground
Workarounds
One workaround involves casting the type (although it disregards the existing type safety):
needsVin(myVehicle as IVehicleNonNullVin);
Alternatively, creating a new model is an option (but may not be ideal for large numbers of properties):
const nonNullVehicle: IVehicleNonNullVin = {
model: myVehicle.model,
vin: myVehicle.vin
}
needsVin(nonNullVehicle);