Skip to content
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 lambda identity function can cheat typesystem easily #8397

Closed
mpodlasin opened this issue Apr 30, 2016 · 6 comments
Closed

Generic lambda identity function can cheat typesystem easily #8397

mpodlasin opened this issue Apr 30, 2016 · 6 comments
Labels
Design Limitation Constraints of the existing architecture prevent this from being fixed

Comments

@mpodlasin
Copy link

mpodlasin commented Apr 30, 2016

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):

function id <A> (a : A) : A {
  return "string";
}

This however DOES (which for me seems super bad):

const id :<A>(a : A) => A = a => "string";

var x : number = id<number>(5); // compiler does not complain at all :(
@malibuzios
Copy link

malibuzios commented Apr 30, 2016

(I'm using version 1.9.0-dev.20160429)

Hi, I tried to rewrite the example a bit differently:

This errors:

let func: <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:

type Func = <T>(a : T) => T;

let func: 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:

let x = 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:

type Func1 = <T>(a: T) => T;
type Func2 = <T>(a: T) => string;

let func1: Func1;
let func2: Func2;

func1 = func2; // no error

And compare it to:

type Func1 = <T>(a: T) => number;
type Func2 = <T>(a: T) => string;

let func1: Func1;
let func2: 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.

@malibuzios
Copy link

malibuzios commented 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 accept void:

declare let func1: <T, V>(a: T, b: V, c: V) => T;
declare let func2: (a: number, b: string, c: void) => void;

func1 = func2; // no error

@iskiselev
Copy link

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.

@RyanCavanaugh RyanCavanaugh added the Design Limitation Constraints of the existing architecture prevent this from being fixed label Aug 18, 2016
@RyanCavanaugh
Copy link
Member

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
Copy link

@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
Copy link
Contributor

mhegazy commented May 18, 2017

tracked by #5616

@microsoft microsoft locked and limited conversation to collaborators Jun 19, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Design Limitation Constraints of the existing architecture prevent this from being fixed
Projects
None yet
Development

No branches or pull requests

6 participants