4

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);

1 Answer 1

2
+500

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

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.