microsoft / microsoft/TypeScript

Index type distributes over intersections too eagerly

Aperta
#61,101 0 commenti 0 reazioni 0 assegnatari Vedi su GitHub

Nessuno ha ancora preso questa issue.

Domain: Indexed Access Types Help Wanted Possible Improvement
Lingua principale
Go
Stelle
111k
Fork
14.3k
Merge medio
2g 4h
PR unite (30g)
132

Descrizione

### 🔎 Search Terms

index keyof indexed access deferred deferral instantiation

### 🕗 Version & Regression Information

- This is the behavior in every version I tried

### ⏯ Playground Link

https://www.typescriptlang.org/play/?ts=5.8.0-dev.20250202#code/C4TwDgpgBAaghgGwK4QM4B4AqA+KBeKTAbQGsIQB7AM0IF0BuAKEYHoWpgAnOAO1SoqcAtqihVOFIVABEZEBAAmUCgCMAVhADGwKELhhpHClDhQkPAJYUejUJCgBRAB5c42hwDcIPYKgBiEkIACnAgCBRwCgCy+li4BPDIaOgA3oxQUEQA0lAWPFBy1HQAXITZtFAAZFApHOAQpTkAvkxN2Ey29Y48AI4oKADy6lrAWA5CFsDAip7eOhAu3gqitXYNUEESkJygWeRQbfg16VBsUJqcEHDTomASTiC6EMAAFhTLJxATwKVpGRnZXL5TDjSbTBSzHxEaRraS0WilAAUJ3+UDAoXCkVKA2+Y2+4MhOmqq3qjQOABoZLDsOSUVAAJT4XAeCgWBRMDItRhczr2ZxaPFgxRQBbTHjLGp1SClTYUba7faHAjIjLeHqlBy9foQIYabSCqaKGmMRl4Zms9nMBRaBBwS5iczaKz5C5XaYAZWAgggBvB2ER1qoeUmzt+n2+qDDqMBeQK5CKIPxigRUER6LCEQUpUTQoU5VN5rZHIOxYWWg1TgFzlc7i8Pn8gRCGciMTAvqN7W59NKvBAHVd1wgnu9iL+IojUf+eQHqEUSIA+r8zGAAEIgUo8JBCFQQTgHAs1Jq01HWmdz1OLyUKCgAdx4a43W53e6aB5SR5OH9Vlc0SLVb7pNUADovkmIDp0uOBZwUUc6QyJBV3XGQVDgAAvPIAHM4GkSkzleCxRFQN4kAQJRdwkTgAEI6VfDpUU0axUB0Kh8gIYDQOAcCeDPS1UTOf4AD0AH5mD49h8NEAioDUJAmJMc45UeIobxea5clEBQCLAW15CUKCoAAA3Y74uJ4gyAHJRDWOkGL4eYnDcYB3TgIQIEweoAEFUA8lQKC8JE4LRDFM2xXFAoyWoEIfKBN23XcDiqSU1lKaQIKuaDDCaRKSWlGQ0qgxRMuPVF-hheppEC41UQPFk2SORE3x5VEFkc5zXPcyAvJ8vyIFgkrl2i6QUPQngsJw05xJeKTrGgcjBFEBjOEubQEBAGj6WLD9aMYIA

### 💻 Code

```ts
type Values = T[keyof T];

// transforms from "keyed object map" to a union
type ExtractEventsFromPayloadMap = Values<{
[K in keyof T]: T[K] & { type: K };
}>;

type EnqueueObject = {
// creates proxy methods
emit: {
[K in TEmittedEvent["type"]]: (
payload: Omit,
) => void;
};
};

type Exec = (
enq: EnqueueObject,
) => void;

declare function createStore(definition: {
emits: {
[K in keyof TEmitted]: (payload: TEmitted[K]) => void;
};
exec: Exec>;
}): any;

createStore({
emits: {
increased: (_: { upBy: number }) => {},
decreased: (_: { downBy: number }) => {},
},
exec: (enq) => {
enq.emit.increased({
upBy: "bazinga", // this should error!
});

const fn = enq.emit.increased;
// ^?

// this is just a copy of what is displayed as `enq.emit.increased`'s type
const exactSameTypeAsAbove: (
payload: Omit<
{ upBy: number } & { type: "increased" } & { type: "increased" },
"type"
>,
) => void = () => {};

exactSameTypeAsAbove({
upBy: "bazinga", // this one errors correctly
});
},
});
```

### 🙁 Actual behavior

There is no error on the annotated call

### 🙂 Expected behavior

It should error

### Additional information about the issue

1. `keyof (T & { type: K })` gets normalized to `keyof T & "type"`
2. that passed through `Exclude` (within `Omit`) is left as `Exclude`.
3. then `T` gets instantiated with `ExtractEventsFromPayloadMap<{ increased: { upBy: number; }; decreased: { downBy: number; }; }`
4. but now `keyof ...` returns only *shared* keys of this **union**
5. the only shared key is `type` so this `Exclude` gets computed as `never` (`Exclude<"type", "type">`)
6. and the final parameter type of `enq.emit.increased` gets computed as just `{}` (from `{ [K in never]: ... }`)

Guida per i contributori

Apri la guida per i contributori

Come iniziare

  1. Leggi tutta la issue e poi la guida ai contributi del progetto.
  2. Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
  3. Fai un fork del repository e lavora su un branch.
  4. Apri una pull request che faccia riferimento al numero della issue.

Direzione di ricerca

Inizia dalla riproduzione collegata in TypeScript Playground e segui il percorso del checker per indexed access, keyof, intersections e Omit. Confronta la chiamata al callback annotata con l’equivalente tipo di funzione scritto esplicitamente. Il lavoro è completato quando la chiamata inferita enq.emit.increased rifiuta una stringa per upBy, con un test di regressione che copra il caso segnalato.

Scritto dal modello di indicizzazione a partire dal testo della issue.

Valutazione

Stack tecnologico
typescript
Ambito
compilers
Tipo di issue
Bug
Difficoltà
4/5
Tempo stimato
3-5 giorni
Stato di attività
Ferma
Chiarezza
Abbastanza chiara
Idoneità per principianti
42/100

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.