Skip to content

NonNullable is accidentally simplified away from generic index accessesΒ #50539

Closed
@Andarist

Description

@Andarist

Bug Report

πŸ”Ž Search Terms

defer, index access, index signature, nonnullable

πŸ•— Version & Regression Information

  • This somewhat changed between versions 4.7 and 4.8

⏯ Playground Link

Playground link with relevant code

πŸ’» Code

interface StateSchema {
  states?: {
    [key: string]: StateSchema;
  };
}

declare class StateNode<TStateSchema extends StateSchema> {
  schema: TStateSchema;
}

// worked in 4.7 but perhaps accidentally
type StateNodesConfigCurrent<TStateSchema extends StateSchema> = {
  [K in keyof TStateSchema["states"]]: StateNode<TStateSchema["states"][K]>;
};

// this OTOH should work right now but it doesn't
type StateNodesConfigNew<TStateSchema extends StateSchema> = {
  [K in keyof TStateSchema["states"]]: StateNode<
    NonNullable<TStateSchema["states"]>[K]
  >;
};

πŸ™ Actual behavior

NonNullable disappears when computing getConstraintFromIndexedAccess. Essentially this type:

NonNullable<TStateSchema["states"]>[keyof TStateSchema["states"]]

gets simplified/distributed to

TStateSchema["states"][string] | TStateSchema["states"][number] | TStateSchema["states"][symbol]

As we might notice here - NonNullable is gone from here because of the simplification rules:

// (T | U)[K] -> T[K] | U[K] (reading)
// (T | U)[K] -> T[K] & U[K] (writing)
// (T & U)[K] -> T[K] & U[K]

The third case applies here so we end up with something like this

TStateSchema["states"][string] & {}[string] | TStateSchema["states"][number] & {}[number] | TStateSchema["states"][symbol] & {}[symbol]

In here, those {}[string | number | symbol] are just reduced to unknown and T & unknown gets reduced to T. So that's how NonNullable "disappears" here.

Note that even though T[number | symbol] is not valid for this T this indexed access is allowed based on this logic:
https://github.dev/microsoft/TypeScript/blob/488d0eebd0556fcc6e5f4cfc69c2ef7e5c2708ed/src/compiler/checker.ts#L16036-L16040

πŸ™‚ Expected behavior

NonNullable should be correctly retained/deferred in this case to preserve the correct constraint of the generic type.

Metadata

Metadata

Assignees

Labels

Fix AvailableA PR has been opened for this issueNeeds InvestigationThis issue needs a team member to investigate its status.

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions