Skip to content

Null-Check is not fully recognized for Partial<T> generic mapped types #42537

Closed
@JakenVeina

Description

@JakenVeina

Bug Report

🔎 Search Terms

mapped
type
null
check
function
parameter

🕗 Version & Regression Information

First encountered today, when attempting to write some code utilizing generic mapped types.

This is the behavior in every version I tried, including 3.3.3 through Nightly, and I reviewed the FAQ for entries about mapped types.

⏯ Playground Link

Here

💻 Code

type Foo<TAnchor extends HTMLElement> = { readonly anchor: TAnchor; };
type FooMap = { readonly [fooKey: string]: Foo<HTMLElement>; };
type FooAnchorMap<TMap extends FooMap> = { readonly [TKey in keyof TMap]: TMap[TKey]["anchor"]; };

function baz(anchor: HTMLElement) { anchor.hidden = false; }

function bar<TMap extends FooMap>(fooKey: keyof TMap, fooAnchors: Partial<FooAnchorMap<TMap>>) {
    const fooAnchor = fooAnchors[fooKey];

    if (fooAnchor == null) {
        throw new Error(`Unable to find key '${fooKey}' in map`);
    }

    // The compiler clearly recognizes that fooAnchor is HTMLElement, as confirmed by intellisense.
    fooAnchor.hidden = true;

    // Error here, indicating that the inferred type of fooAnchor is 'TMa[[keyof TMap]["anchor"] | undefined', as confirmed by intellisense
    baz(fooAnchor);
}

🙁 Actual behavior

The variable fooAnchor is inferred to be possibly undefined, almost immediately after a clear null-check. However, it's somehow only inferred to be possibly undefined when passed as a function argument. When used directly, the null-check is recognized.

🙂 Expected behavior

Ideally, the null-check should be recognized fully, by all uses of fooAnchor after the null-check occurs. However, if this is somehow not feasible, the inferred type of fooAnchor should at least be consistent. I.E. it should not be possible for the compiler to infer the variable's differently at different points within the same narrowing scope (I.E. when no type-narrowing or type-widening operations have occurred).

This seems to be very closely related to #31908. However, I thought it was appropriate to make a separate issue, because this situation doesn't completely line up with the situations described in that issue. Most significantly...

TypeScript doesn't know that partial[key] is equivalent to T[K] | undefined, only that it's assignable to that type.

This doesn't seem to be true here, because A) the error, and intellisense clearly report the inferred type to be ... | undefined and B) the variable is able to be used directly as if it's not possibly undefined. It's only when passing the variable as an argument that the error occurs.

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