You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Type error when using generic function result as argument, but not when assigned to temp variable first #35339
declareconstfoo: <R1,F1extends()=>R1,R2,F2extends(input: ReturnType<F1>)=>R2>(f1: F1,f2: F2)=>anyconstf1=()=>({a: 'a'})constf2=(input: {a: string})=>({b: 'b'})foo(f1,f2)// GoodconstmakeF1=()=>f1;foo(makeF1(),f2)// GoodconstmakeF1p=<T>(t: T)=>f1;foo(makeF1p(''),f2// Error WHY :();constmade=makeF1p('');foo(made,// This is finef2)
Expected behavior:
Both foo() calls should compile as the only difference is whether the result of a function call is assigned to a temporary first.
Actual behavior:
The first call to foo(), where makeF1p() is called inline, produces a type error at compile time. The second, where the return value of makeF1p() is assigned to temp variable first, compiles fine. The error is:
Argument of type '(input: { a: string; }) => { b: string; }' is not assignable to parameter of type '(input: unknown) => unknown'.
Types of parameters 'input' and 'input' are incompatible.
Type 'unknown' is not assignable to type '{ a: string; }'.
It almost seems like the type parameter inference machinery is leaking outside the scope of the call expression or something? It's weird, at any rate.
constfn=<A,B>({ a, b, c }: {a: ()=>A;b: (a: A)=>B;c: (a: A,b: B)=>any;})=>{constt=a();returnc(t,b(t));};// wrongfn({a: ()=>1,b: (a)=>1+a,// prop b type is (a: number) => unknownc: (a,b)=>a+b,// arg b is unknown});// wrongfn({a: ()=>1,b: (_)=>1,// prop b type is (a: number) => unknownc: (a,b)=>a+b,// arg b is unknown});// correctfn({a: ()=>1,b: ()=>1,// prop b type is (a: number) => numberc: (a,b)=>a+b,// arg b is number});
exporttypeConstructor<T=object,Aextendsany[]=any[],Static={}>=(new(...a: A)=>T)&Static// this is used for type casting in mixinsexportfunctionConstructor<T=object,Static={}>(Ctor: Constructor<any>): Constructor<T>&Static{return(Ctorasunknown)asConstructor<T>&Static}
This issue was not happening before (the second example would work), so it seems to be from some relatively new version of TypeScript within the past few months.
I’m almost positive it’s a bug. Type inference for a generic call shouldn’t be affected by whether the result is assigned to a variable vs. directly passing it to another generic function.
Deleted my previous comments, I was thinking something else. Yeah in Bruce Pascoe (@fatcerberus)'s example above it seems like the generic args for foo should first be inferred, then that should guide inference for bar.
This is a fairly normal consequence of having too many type parameters in the absence of unification. We'd need to have an iterative inference algorithm to process this call as expected. The root problem is that there's a candidate via R1 being fed to f2's T via the return type, which is why you only see this when it's in the argument position.
I would rewrite foo to have fewer type parameters, if applicable:
declarefunctionfoo<T={}>(): number&T;declarefunctionbar<T>(x: T): void;bar(foo());// T = neverlettmp=foo();// T = {}bar(tmp);
In this case, there is only a single type parameter involved, but appears to trigger the same issue. Is it actually the same issue, or is this something else? Because I really don't expect the presence--or lack thereof--of an intermediate assignment (which is itself inferred!) to affect the inference of the function call... and yet it does.
Activity
eyelidlessness commentedon Nov 25, 2019
I don't know if this will help, but I was able to work around this by referencing the type parameter
TinmakeF1p's return type:jcalz commentedon Nov 26, 2019
I think we can remove
R1andR2and still reproduce withPlayground link
shenjunru commentedon Dec 12, 2019
Similar case: Playground link
fatcerberus commentedon Dec 14, 2019
Smaller repro, pretty sure this is the same issue:
Contextual typing seems to be causing chained generic calls to influence each others' type parameters.
trusktr commentedon Dec 14, 2019
I'm not sure if this is a bug (regression) or a (new) feature.
It adds one more thing that can make class-factory mixins brittle. F.e., I have to consciously write
instead of
where the definition of
ConstructorisThis issue was not happening before (the second example would work), so it seems to be from some relatively new version of TypeScript within the past few months.
fatcerberus commentedon Dec 14, 2019
I’m almost positive it’s a bug. Type inference for a generic call shouldn’t be affected by whether the result is assigned to a variable vs. directly passing it to another generic function.
trusktr commentedon Dec 16, 2019
Deleted my previous comments, I was thinking something else. Yeah in Bruce Pascoe (@fatcerberus)'s example above it seems like the generic args for
fooshould first be inferred, then that should guide inference forbar.RyanCavanaugh commentedon Jan 7, 2020
This is a fairly normal consequence of having too many type parameters in the absence of unification. We'd need to have an iterative inference algorithm to process this call as expected. The root problem is that there's a candidate via
R1being fed tof2'sTvia the return type, which is why you only see this when it's in the argument position.I would rewrite
footo have fewer type parameters, if applicable:trusktr commentedon Jan 7, 2020
What's the performance trade off of adding an "iterative inference algorithm"?
fatcerberus commentedon Jan 8, 2020
Ryan Cavanaugh (@RyanCavanaugh)
In this case, there is only a single type parameter involved, but appears to trigger the same issue. Is it actually the same issue, or is this something else? Because I really don't expect the presence--or lack thereof--of an intermediate assignment (which is itself inferred!) to affect the inference of the function call... and yet it does.