microsoft / microsoft/TypeScript

Explicit target type breaks generic parameter inference for function call with intersection return type

Aperta
#61,467 1 commento 0 reazioni 0 assegnatari Vedi su GitHub

Nessuno ha ancora preso questa issue.

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

Descrizione

### 🔎 Search Terms

explicit target type breaks generic parameter inference function call intersection return type contextual typing

### 🕗 Version & Regression Information

- This is the behavior in every version I tried, and I reviewed the FAQ for entries about "explicit"

### ⏯ Playground Link

https://www.typescriptlang.org/play/?strictBuiltinIteratorReturn=true#code/GYVwdgxgLglg9mABMAjAHgIKIKYA8rZgAmAzogNbYCecwiAEgCoCyAMgKIA22AtoVIwCGAcwBygvs0EAHADSIAQgD4AFIIBciDPIBGmhQEpNTNl178hYidinSA2hgC6iAGSLEAbwCwAKESIAJ2woEACkAHkdACtsaAA6QRISGGEwFSI4CBA+MCg4iCDBAjMcqDUDXQMAbl8AX19QSFgEZAAmTBx8QlJPWvllNU1tRD1FIy1Xd28-QODQiOjYvMTk1LVKmp96n18Ael3ERgALGDJgQRhOMh1YwRASbEOAZUQYMGBsALIFRABeBhYrAUICgUAQJX4kw8CAgnBgEHImhUBj+SkQADc4DAiLVXmASARBERELR3P9oZA4QikSjfmjMdjthAEATEAQCShjIDgaDwdxSlCYVTEYhkaiMVicX9kCgVAAiHQgsFgOXyCmw+EixrQeBpAweWq1ap7A7HU6IADucAC5DI2HRhDZRzgIGERydjzeHwCQWJUCo0k9ZCgR0eJGsiESHs60ipMCgbIDj0EOjgDt8zPxCfZUFa-nzBcLReLJdL-1Q8sVvJVaqFms02uaeoNRs2+0OJzIVpt11u90ewkIn3hiGkggC1gIX0jQUQwhgDqQeFj8PjnCoGZZ2ewBIAzFy2DzlRDcoLKfXRbT6ZLceX0AqlQhVZ469TL+KGTjVA-q8-1cKG3AHUEGRFtjR8dszS7a1bRGPsHhnAI4AtZAgKbMhx0eMA4ATQRgA+aBsGJYBrUQEg4D4WZEgQTcszZHcoAAFgPIFHzAE8Ezcf8LzFOkJUZaUKx-ZU-1fEVeLRMC21NTtLRg3sIDuBCvU+QgIEebtYJuUFPkteNnRBRNA0QHgZFo1kcwAVhYo8+XMU8uLEmkPxvQTWnSTJsn4fJCmKfl+ErNi5QqF9zzfRtdVAw1wKAA

### 💻 Code

```ts
function f1(a: A, b: B): HTMLElementTagNameMap[A] & B {
return Object.assign(document.createElement(a), b);
}
function f2
(a: A, b: B): A & B {
return Object.assign(a, b);
}

// This fails because TS infers B = HTMLButtonElement & {onclick: () => void} instead of B = {onclick: () => void}
const test1: HTMLButtonElement & {onclick: () => void} = f1("button", {onclick: function(){}});
// This works even though the inferred type is the same as the explicit type above
const test2 = f1("button", {onclick: function(){}});
// This works because generic parameters are given explicitly
const test3: HTMLButtonElement & {onclick: () => void} = f1<"button", {onclick: () => void}>("button", {onclick: function(){}});
// This works because arrow functions are not affected for some reason
const test4: HTMLButtonElement & {onclick: () => void} = f1("button", {onclick: () => {}});
// This works because inference works better without type map
const test5: HTMLButtonElement & {onclick: () => void} = f2(document.createElement("button"), {onclick: function(){}});
```

### 🙁 Actual behavior

`test1` shows an error:
> Argument of type '{ onclick: (this: GlobalEventHandlers) => void; }' is not assignable to parameter of type 'HTMLButtonElement & { onclick: () => void; }'.

This is because TS sees that `test1` is explicitly typed `HTMLButtonElement & {onclick: () => void}`, and so it erroneously infers the generic type parameters of `f1` as `B = HTMLButtonElement & {onclick: () => void}`, even though the correct inference is `B = {onclick: () => void}`.

More interestingly, `B` is inferred correctly if the explicit target type is omitted, as demonstrated with `test2`. However, the inferred type of `test2` is the same as the explicit type of `test1`!

### 🙂 Expected behavior

If a program is valid with an inferred type, then it should also be valid when that type is turned explicit.

### Additional information about the issue

The code snippet also demonstrates that this issue is somehow related to using mapped types and `function(){}` instead of `() => {}`. While curious, I think the main issue is that `test1` and `test2` behave differently.

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 con la riproduzione collegata in TypeScript Playground e confronta test1 fino a test5, concentrandoti sull'inferenza dei parametri generici per f1 e sul relativo tipo di ritorno di intersezione. Traccia il comportamento del checker responsabile della tipizzazione contestuale di function(){}, quindi usa gli esempi segnalati come casi di regressione. Il lavoro è completato quando test1 viene accettato in modo coerente insieme a test2 senza rompere gli altri casi di inferenza.

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

Valutazione

Stack tecnologico
typescript
Ambito
compilers
Tipo di issue
Bug
Difficoltà
5/5
Tempo stimato
Più di una settimana
Stato di attività
Ferma
Chiarezza
Abbastanza chiara
Idoneità per principianti
30/100

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.