Search code examples
typescriptnarrowing

Type narrowing object properties with a function


I have a complicated type with many nested properties. I want type narrowing. This seems to work for basic primitives, but when an object is involved, TS cannot seem to narrow the properties using the same principles.

type Test = {
    property: string | null
}

const propIsNotNull = (data: Test): boolean => {
    return (data.property !== null)
}

const testData: Test = {
    property: 'nonull'
}

if (propIsNotNull(testData)) {
    console.log(testData.property.toUpperCase())
    //          ~~~~~~~~~~~~~~~~~
    // 'testData.property' is possibly 'null'.(18047)
}

I can of course change it to the following, but it makes the code harder to read

if (testData.property !== null) {
    console.log(testData.property.toUpperCase())
}

I don't really want to make a new type just so I can use a type guard. I feel like I am missing something fundamentally simple.


Solution

  • A function without a type predicate will never have any narrowing capabilities. You also don't need a new type just to make a type predicate. There are multiple ways to reuse the type and just modify the part that you are checking.

    One possible way would look like this:

    const propIsNotNull = (data: Test): data is Test & { 
        property: Exclude<Test["property"], null> 
    } => {
        return (data.property !== null)
    }
    

    The type predicate signals to the caller that data is of type Test and that the property property is not null if the condition is true.


    Playground