Explicit target type breaks generic parameter inference for function call with intersection return type
#61 467 ouverte le 23 mars 2025
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
🔎 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
💻 Code
function f1<A extends keyof HTMLElementTagNameMap, B>(a: A, b: B): HTMLElementTagNameMap[A] & B {
return Object.assign(document.createElement(a), b);
}
function f2<A extends {}, B>(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.