Skip to content

Generic lookup type wrongly narrowed to never with strictNullChecks #26130

Closed

Description

I ran across this while migrating my project to strictNullChecks and it's positively bizarre.

TypeScript Version: master (50f442f)

Search Terms: mapped type narrow narrowing null strictNullChecks never

Code (with strictNullChecks)

let mappedObject: {[K in "foo"]: null | {x: string}} = {foo: {x: "hello"}};
declare function foo<T>(x: T): null | T;

function bar<K extends "foo">(key: K) {
  const element = foo(mappedObject[key]);
  if (element == null)
    return;
  const x = element.x;
}

Expected behavior: Compile without error.

Actual behavior: Error: Property 'x' does not exist on type 'never'.

Playground Link: link (remember to enable strictNullChecks)

Related Issues: Didn't find any.

Activity

  1. RyanCavanaugh commented on Aug 1, 2018

    @RyanCavanaugh
    Member

    This is just weird. Writing

    const x = element!.x;

    eliminates the error, but there shouldn't be anything for ! to take away

  2. RyanCavanaugh commented on Aug 1, 2018

    @RyanCavanaugh
    Member

    Not a regression FWIW

  3. weswigham commented on Aug 2, 2018

    @weswigham
    Member

    This is just weird. Writing
    const x = element!.x;
    eliminates the error, but there shouldn't be anything for ! to take away

    FWIW !, as a cast, also resets a control flow-narrowed type to its declared type. We have an open issue to make it respect narrowing: #16945 (I was looking at it recently)

  4. mattmccutchen commented on Aug 2, 2018

    @mattmccutchen
    ContributorAuthor

    I found the problem: in getAssignmentReducedType, we have a declared type of {x: string} | null and an assigned type of {foo: {x: string} | null}[K] | null. When we ask if the assigned type is maybe assignable to {x: string}, we decompose the first level of union and check if {foo: {x: string} | null}[K] is assignable to {x: string}, but it isn't because its constraint is {x: string} | null. So we throw {x: string} out of the declared type and are left with null, and after the null check, we are left with never.

    Using the comparable relation in place of typeMaybeAssignableTo fixes this issue. I'm going to see what else breaks if I do that.

  5. added a commit that references this issue on Aug 2, 2018
    d45e422
  6. mattmccutchen commented on Aug 2, 2018

    @mattmccutchen
    ContributorAuthor

    Proposed fix is in #26143.

  7. added a commit that references this issue on Aug 20, 2018
  8. locked as resolved and limited conversation to collaborators on Oct 21, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

BugA bug in TypeScriptFixedA PR has been merged for this issue

Type

No type

Projects

No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions