microsoft / microsoft/TypeScript
Explicit target type breaks generic parameter inference for function call with intersection return type
- Langage dominant
- Go
- Étoiles
- 111k
- Forks
- 14.3k
- Merge moyen
- 2 j 4 h
- PR mergées (30 j)
- 132
Description
### 🔎 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.
Guide de contribution
Ouvrir le guide de contribution
Piste de recherche
Commencez par la reproduction liée dans TypeScript Playground et comparez test1 à test5, en vous concentrant sur l’inférence des paramètres génériques pour f1 et son type de retour d’intersection. Suivez le comportement du checker responsable du typage contextuel de function(){}, puis utilisez les exemples signalés comme cas de régression. C’est terminé lorsque test1 est accepté de manière cohérente avec test2 sans casser les autres cas d’inférence.
Rédigé par le modèle d'indexation à partir du texte de l'issue.
Évaluation
- Stack technique
- typescript
- Domaine
- compilers
- Type d'issue
- Bug
- Difficulté
- 5/5
- Temps estimé
- Plus d'une semaine
- Activité
- À l'abandon
- Clarté
- Plutôt claire
- Accessibilité débutants
- 30/100