Skip to content

keyof NonNullable<T> cannot be used to index type T #23368

Closed

Description

TypeScript Version: 2.9.0-dev.20180412

Code

function assign<T extends object>(to: T, from: T | undefined) {
	for (const p in from!)
		to[p] = from![p];
	return to;
}

Expected behavior:

No error.

Actual behavior:

src/a.ts(3,9): error TS2536: Type 'keyof NonNullable<T>' cannot be used to index type 'T'.

Related issues: #19461

Activity

  1. weswigham commented on Apr 12, 2018

    @weswigham
    Member

    Partial dupe of #22934 ? In any case, also should also be "fixed" by #22971

    The root cause is still that NonNullable<T> and T are (rightly) unrelated in this direction, but when T extends object, one expects NonNullable<T> to just be T.

  2. weswigham commented on Apr 12, 2018

    @weswigham
    Member

    Andy (Andrewkraft) (@Andy-MS) I think that's just because narrowing doesn't apply higher order types right now.

  3. dhruvrajvanshi commented on Apr 30, 2019

    @dhruvrajvanshi
    Contributor

    Hey everyone. I was trying to figure this issue out and I realized that keyof <Higher order type> reduces to string | number | symbol which is causing this problem.

    If this issue is worth fixing, I suggest adding the following rule to the isTypeRelated relation.

    keyof X extends keyof Y
    when
        Y extends X
    

    This rule should be applied before the rule that reduces keyof Y to string | number | symbol because that loses the type relationship between X and Y

    Example of the first rule being applied:

    function f<X, Y extends X>(keyofX: keyof X, keyofY: keyof Y) {
        keyofY = keyofX; // fine because keyof X is a subset of keyof Y
        keyofX = keyofY; // error because Y may have more keys than X
    }
    

    Right now, an error is raised, but for the wrong reasons.

    It tries to assign string | number | symbol to keyofX after reducing keyof Y which is fine, but would miss out on edge cases like this.

    The original issue doesn't occur anymore but this does

    function f1<T>(keyofT: keyof T, keyofNonNullT: keyof NonNullable<T>) {
    	keyofT = keyofNonNullT // should not raise error but does on the latest master
    }

    EDIT:

    Okay, I implemented the new rule but turns out this isn't quite enough to fix the second example because
    NonNullable<T> extends T is false. I think this would require another rule for conditional types.

  4. dhruvrajvanshi commented on Apr 30, 2019

    @dhruvrajvanshi
    Contributor

    Update:

    I implemented a special case for checks like
    (keyof isRelatedTo keyof ( extends ? ... : ...))
    right before the checker gives up and reduces the source type to string | number | symbol which works as expected.

    Is that kind of logic too much of a special case for the type checker? NonNullable has become kinda pervasive so I don't know.

  5. 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

Assignees

No one assigned

    Labels

    BugA bug in TypeScriptFix AvailableA PR has been opened for this issue

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions