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
Generic lambda identity function can cheat typesystem easily #8397
Hi. Before I start I want to emphasize that I googled a lot for that problem and found nothing. That being said, my problem seems to be so simple, that I am sure it was discussed before. If that is the case forgive me for spam. I would be just grateful for some link to the explanation of why TS works here the way it works.
I use TypeScript 1.8.10
So... this does not compile (which is good):
functionid<A>(a : A) : A{return"string";}
This however DOES (which for me seems super bad):
constid :<A>(a : A)=>A=a=>"string";varx : number=id<number>(5);// compiler does not complain at all :(
Hi, I tried to rewrite the example a bit differently:
This errors:
letfunc: <T>(a: T): T=>"abcd";// error: string is not assignable to T
However when the signature is declared separately, as a type. This invalid return type can be 'hidden' under the declared type, as the call signature type <T>(a: T) => string is allowed to be assigned to the call signature type <T>(a: T) => T:
typeFunc=<T>(a : T)=>T;letfunc: Func=<T>(a: T): string=>"abcd";// no error
And then a caller can use any type for T and the compiler would still consider the return type to be T and not string:
letx=func(1234);// 'x' gets type 'number' but the actual value "abcd".
It looks as if the compiler considers these function types compatible, in particular the return type string is seen to be assignable to T.
I'll try to isolate and rewrite it as clearly as possible:
typeFunc1=<T>(a: T)=>T;typeFunc2=<T>(a: T)=>string;letfunc1: Func1;letfunc2: Func2;func1=func2;// no error
And compare it to:
typeFunc1=<T>(a: T)=>number;typeFunc2=<T>(a: T)=>string;letfunc1: Func1;letfunc2: Func2;func1=func2;// error: type 'string' is not assignable to 'number'
I'm aware that function parameters are bivariant, and based on my tests function return types should be covariant. I'm not sure if that has anything to do with this though. There isn't much further information I can provide at this point.
#3410 includes some information (although almost a year old), explaining this check (or a related one, I'm not sure) is avoided to due performance reasons. #5616 is very similar to this (though the example there shows the return type assignment in the other direction), and seems to be 'In Discussion'.
Seems like almost any function can be assigned from or to a generic signature. Parameters and the return type are considered to be of type any, so they will even accept void:
I'm not sure, if this should be looked in this issue, but currently TypeScript also doesn't look on generic type constraints when it check functions types:
let f1: (input:number)=>{field:number};
let f2: (input:number)=>{field:string};
f1 = f2; // Type is not assignable error
f2 = f1; // Type is not assignable error
let gf1: <T extends {field:number}>(input:number)=>T;
let gf2: <T extends {field:string}>(input:number)=>T;
gf1 = gf2; // No error here, but should be.
gf2 = gf1; // No error here, but should be.
Unfortunately signatures are seen in their "erased" form (with type parameters replaced with their constraints) during assignability checks. This is something we're aware of as a hole but don't yet have a solution that gives satisfactory performance,
Ryan Cavanaugh (@RyanCavanaugh) if performance is a problem (presumably due to recursive deep dives) why not limit checks to a certain depth that can be configured from tsconfig.json, who don't care about typesafety sets "depth: 0" and production ready builds "depth: 100"?
Activity
malibuzios commentedon Apr 30, 2016
(I'm using version
1.9.0-dev.20160429)Hi, I tried to rewrite the example a bit differently:
This errors:
However when the signature is declared separately, as a type. This invalid return type can be 'hidden' under the declared type, as the call signature type
<T>(a: T) => stringis allowed to be assigned to the call signature type<T>(a: T) => T:And then a caller can use any type for
Tand the compiler would still consider the return type to beTand notstring:It looks as if the compiler considers these function types compatible, in particular the return type
stringis seen to be assignable toT.I'll try to isolate and rewrite it as clearly as possible:
And compare it to:
I'm aware that function parameters are bivariant, and based on my tests function return types should be covariant. I'm not sure if that has anything to do with this though. There isn't much further information I can provide at this point.
malibuzios commentedon Apr 30, 2016
Further investigation:
#3410 includes some information (although almost a year old), explaining this check (or a related one, I'm not sure) is avoided to due performance reasons.
#5616 is very similar to this (though the example there shows the return type assignment in the other direction), and seems to be 'In Discussion'.
Seems like almost any function can be assigned from or to a generic signature. Parameters and the return type are considered to be of type
any, so they will even acceptvoid:iskiselev commentedon Jun 16, 2016
I'm not sure, if this should be looked in this issue, but currently TypeScript also doesn't look on generic type constraints when it check functions types:
RyanCavanaugh commentedon Aug 18, 2016
Unfortunately signatures are seen in their "erased" form (with type parameters replaced with their constraints) during assignability checks. This is something we're aware of as a hole but don't yet have a solution that gives satisfactory performance,
zpdDG4gta8XKpMCd commentedon Dec 20, 2016
Ryan Cavanaugh (@RyanCavanaugh) if performance is a problem (presumably due to recursive deep dives) why not limit checks to a certain depth that can be configured from tsconfig.json, who don't care about typesafety sets "depth: 0" and production ready builds "depth: 100"?
mhegazy commentedon May 18, 2017
tracked by #5616