-
Notifications
You must be signed in to change notification settings - Fork 12.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weβll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Generic function does not accept argument of type with union and intersection #44315
Comments
Also, it is worth to note that the compiler made the wrong conclusion in the error message when it reveals |
What was the intent in writing declare function testA<Param extends A>(param: Param & A): Param; instead of declare function testA<Param extends A>(param: Param): Param; ? |
This was an example that simplifies the real-life piece of code exposing the bug. This pattern is useful in a generic function where types are not narrowing properly due to design limitation: type Animal = { type: 'dog'; forelegs: number } | { type: 'duck'; wings: number };
function calculateForelimbs<A extends Animal>(animal: A): A & { forelimbs: number } {
return { ...animal, forelimbs: animal.type === 'dog' ? animal.forelegs : animal.wings };
// won't work due to design limitation
} It could be fixed by adding function calculateForelimbsWorkable<A extends Animal>(animal: A & Animal): A & { forelimbs: number } {
return { ...animal, forelimbs: animal.type === 'dog' ? animal.forelegs : animal.wings };
// working like a charm
}
function createSuperAnimal(): Animal {
const legsOrWings = 100500;
const typeOfAnimal = Math.random() < 0.5 ? 'dog' : 'duck';
return typeOfAnimal === 'dog'
? { type: typeOfAnimal, forelegs: legsOrWings }
: { type: typeOfAnimal, wings: legsOrWings }
}
const superAnimalWithLimbsCalculated = calculateForelimbsWorkable(createSuperAnimal()); If we make the type Animal = ({ type: 'dog'; forelegs: number } | { type: 'duck'; wings: number }) & { domesticated: boolean }; it still works (link to playground). When we decide to have property type Animal = ({ type: 'dog'; forelegs: number } | { type: 'duck'; wings: number }) & { domesticated?: boolean }; Now, when version 4.3.2 with improved contextual narrowing for generics has been shipped, it has become less actual. Now we can skip But the bug is a bug. It may affect the legacy code or be a broken part of a bigger problem. |
I found the use case where this pattern would be still useful: type A = ({ a: any } | { b: any }) & { c?: any };
declare function f<X extends A>(x: X): X
function testA1<Param extends A | undefined>(param: Param): Param {
return param && f(param)
// won't work even in typescript 4.3
};
function testA2<Param extends A | undefined>(param: Param & A): Param {
return param && f(param)
// working fine
};
declare const s1: A;
const a1 = testA2(s1); // still error
type B = ({ a: any } | { b: any }) & { c: any };
declare const s2: B;
const a2 = testA2(s2); // wrong type A | undefined, must be B
const a3 = testA1(s1); // A, but testA1 would not work on its own
const a4 = testA1(s2); // B, but testA1 would not work on its own It works fine in typescript 3.3.3333: link |
Bug Report
π Search Terms
generic function union intersection type optional property
π Version & Regression Information
β― Playground Link
Playground link with relevant code
π» Code
π Actual behavior
The line
const a1 = testA(s1);
gives an error:In the line
const a2 = testA(s2);
the typescript compiler infers the constantaa2
has typeA
π Expected behavior
The variable
s1
has the typeA
that meansfunction testA<Param extends A>(param: Param & A): Param
acceptss1
. The parameter typeParam
is instantiated toA
.The constant
a2
has the typeB
since the parameters2
has the typeB
.It was a normal behavior up to version 3.6.0-dev.20190723. Since then it leads to errors.
The text was updated successfully, but these errors were encountered: