Skip to content

Adding explicit type that is the same as the inferred type prevents narrowing later on #51757

Closed
@winstliu

Description

@winstliu

Bug Report

I work in a codebase that recommends adding explicit types. In this case, adding a type that is the same as the inferred type (as claimed by the hover tooltips) causes narrowing to fail for a previous maybe-null type that depends on the result that has the explicit type.

🔎 Search Terms

explicit type, narrowing

🕗 Version & Regression Information

Prior to 4.4, both explicit & inferred types would complain that obj could be null.
After 4.4 (and up to 5.0-nightly), the inferred type behaves correctly, but the explicit type still does not see that obj can no longer be null.

⏯ Playground Link

Playground link with relevant code

💻 Code

interface DeeplyNestedObject {
    deeply: {
        nested: {
            object: string;
        }
    }
}

function example(): DeeplyNestedObject | null {
    return Math.random() > 0.5 ? {
        deeply: {
            nested: {
                object: 'hi'
            }
        }
    } : null;
}

function explicit(): DeeplyNestedObject | undefined {
    const obj = example();
    // Explicit type which is the same as the inferred type
    const result: string | undefined = obj?.deeply?.nested?.object;
    if (!result) {
        return undefined;
    }

    // obj can no longer be null, as we've validated that result is truthy which is only possible if obj != null.

    // ts2322: Type 'DeeplyNestedObject | null' is not assignable to type 'DeeplyNestedObject | undefined'.
    // Type 'null' is not assignable to type 'DeeplyNestedObject | undefined'.
    return obj;
}

function inferred(): DeeplyNestedObject | undefined {
    const obj = example();
    // Inferred type
    const result = obj?.deeply?.nested?.object;
    if (!result) {
        return undefined;
    }

    return obj;
}

🙁 Actual behavior

TypeScript claims that obj can still be null, even though we've eliminated that possibility by performing some optional chaining with obj and then checking to make sure that the result was truthy.

🙂 Expected behavior

The same as the inferred example - no compiler errors.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Design LimitationConstraints of the existing architecture prevent this from being fixed

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions