Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Convert interface with nullable string property to string property

I have the following two interfaces, one which allows a nullable vin, the other that doesn't:

interface IVehicle {
    vin: string | null;
    model: string;
}

interface IVehicleNonNullVin {
    vin: string;
    model: string;
}

I want to able to convert a model from IVehicle to IVehicleNonNullVin in an execution path where I'm able to infer that vin is not null.

Consider this 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'
}

Which returns the following error:

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 I'm positive the property isn't nullable here.

Q: How can I convince TS that the existing model conforms to the type based on type checks in the code flow?

Demo in TS Playground


Workarounds

As a workaround, I can cast the type (but that ignores the existing type safety):

needsVin(myVehicle as IVehicleNonNullVin);

Or build a new model (but this doesn't scale well with lots of properties):

const nonNullVehicle: IVehicleNonNullVin = {
    model: myVehicle.model,
    vin: myVehicle.vin
}
needsVin(nonNullVehicle);
like image 639
KyleMit Avatar asked Oct 14 '22 20:10

KyleMit


1 Answers

You can use a type predicate to define a user-defined type guard like this:

const isNonNullVin = (vehicle: IVehicle): vehicle is IVehicleNonNullVin =>{
    return vehicle.vin !== null
}

if (!isNonNullVin(myVehicle)) {
    throw new Error('null');
} else {
    needsVin(myVehicle);
}

TypeScript will narrow that variable to that specific type if the original type is compatible.

Demo in TS Fiddle

like image 199
Simon Tran Avatar answered Oct 20 '22 23:10

Simon Tran