Microsoft/TypeScript

Fails to expand indexed access type when used as a generic type parameter

Open

#54 776 ouverte le 26 juin 2023

Voir sur GitHub
 (1 commentaire) (0 réactions) (0 assignés)TypeScript (6 726 forks)batch import
Domain: Indexed Access TypesHelp WantedPossible Improvement

Métriques du dépôt

Stars
 (48 455 stars)
Métriques de merge PR
 (Merge moyen 6j 17h) (9 PRs mergées en 30 j)

Description

Bug Report

This logic is part of a proprietary DSL where the user wants to work with a number of types, specified as a record. Most of the time the user calls a function similar to

declare function someOtherFunction<T extends DiscriminatorRecord>(record: T): { [K in keyof T]: DiscriminatedUnion<T[K]> }

in which case there are no problems with the inference, but sometimes T needs to be restricted to have certain keys and values, as shown in the code below. One part of the problem that I wasn't able to reproduce in a code example was the fact that it did work with four types in the union, but failed when I added a fifth. Whereas in the example it fails with any number except one. I could spend more time on reproduction, but hopefully this will be helpful to start with.

🔎 Search Terms

generic indexed access type inference

🕗 Version & Regression Information

  • This is the behavior in every version (5.0.4, 5.1.3, v5.2.0-dev.20230625) I tried, and I reviewed the FAQ for entries about Type System Behavior, Generics

⏯ Playground Link

Playground link with relevant code

💻 Code

type Discriminator =
  | 'First'
  | 'Second'
  | 'Third'

type DiscriminatedUnion<D extends Discriminator> = { discriminator: D } & (
  | { discriminator: 'First', first: boolean }
  | { discriminator: 'Second', second: boolean }
  | { discriminator: 'Third', third: boolean }
  )

type DiscriminatorRecord = Record<string, Discriminator>

function someFunction<T extends DiscriminatorRecord & { override: 'Third' }>() {
  type DiscriminatorFromT = T['override'] // If this expands to 'Third'

  type DiscriminatedFromLiteral = DiscriminatedUnion<'Third'> // then this

  type DiscriminatedFromT = DiscriminatedUnion<DiscriminatorFromT> // should be equivalent to this

  const f1 = (d: DiscriminatedFromLiteral) => d.third; // yet this works
  const f2 = (d: DiscriminatedFromT) => d.third; // but not this
}

🙁 Actual behavior

const f2 = (d: DiscriminatedFromT) => d.third; fails with

Property 'third' does not exist on type 'DiscriminatedUnion<DiscriminatorFromT>'.
  Property 'third' does not exist on type '{ discriminator: DiscriminatorFromT; } & { discriminator: "First"; first: boolean; }'.

🙂 Expected behavior

The property third is inferred to exist on d in const f2 = (d: DiscriminatedFromT) => d.third;

Guide contributeur