Type assertions and optional chaining in Typescript

Viewed 47

I have the following function that performs a simple type assertion, checking that a variable is not undefined or null.

const isDefinedAndNotNull = <T>(val: T | null | undefined): val is NonNullable<T> =>
    (val !== null) && (typeof val !== "undefined");

However, I have an issue when using this function to assert the presence of a nested object using optional chaining. The below code will cause Typescript to not compile:

type MaybeNullThing = {
    myProp?: {
        coolThing?: string
    }
}

const thing: MaybeNullThing = getThing();

if (isDefinedAndNotNull(thing?.myProp?.coolThing)) {
    console.log(thing.myProp.coolThing); // error: thing.myProp could be undefined
}

However, Typescript has no problem with the following:

if (thing?.myProp?.coolThing) {
    console.log(thing.myProp.coolThing); // defined here
}

My question is essentially how to get the type assertion to assert that both the nested object and the target object are defined, if this is possible. Thanks!

Link to a TS Playground containing the above code

1 Answers

The type assertion only really covers the deepest level; the property you're passing to the assertion.
Your condition only confirms coolThing is defined and not null.

To cover myProp, you'd need to explicitly check that as well:

if (isDefinedAndNotNull(thing?.myProp) && isDefinedAndNotNull(thing?.myProp?.coolThing)) {
  console.log(thing.myProp.coolThing);
}

TS is naïve like that, and I honestly don't know of a way around it.

Related