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

Generics not being inferred in complex case #9107

Closed
michaelmesser opened this issue Jun 12, 2016 · 6 comments
Closed

Generics not being inferred in complex case #9107

michaelmesser opened this issue Jun 12, 2016 · 6 comments
Labels
Design Limitation Constraints of the existing architecture prevent this from being fixed

Comments

@michaelmesser
Copy link

michaelmesser commented Jun 12, 2016

TypeScript Version:

Typescript playground

Code

const compose = <In, Intermediate_Big, Intermediate_Small extends Intermediate_Big, Out>(f: (x: In)=> Intermediate_Small, g: (x: Intermediate_Big) => Out): (a1: In) => Out => (_ => g(f(_)))
const first = <T>(a: T[]): T => a[0]
const getText = (_: HTMLElement) => _.innerText
const c = compose(first, getText)

Expected behavior:
c is of type (x: HTMLElement[]) => string
Actual behavior:
c is of type (x: {}) => string

@Artazor
Copy link
Contributor

Artazor commented Jun 12, 2016

Your case could be reformulated in much more simpler manner (even without extends constraint)

type Fn<In, Out> = (arg: In) => Out
const compose = <A, B, C>(f:Fn<A,B>, g:Fn<B,C>) => (arg: A) => g(f(arg)); 

const f = (x: string) => x;
const g = <T>(x: T) => x;

const h1 = compose(f, f); // inferred h1: (arg: string) => string
const h2 = compose(f, g); // inferred h2: (arg: string) => string
const h3 = compose(g, f); // inferred h3: (arg: {}) => string  
const h4 = compose(g, g); // inferred h4: (arg: {}) => {} 

First two cases are ok for you. In the third case you would expect to have h3: (arg: string) => string
and according to this logic in the fourth case - you'd like to have h4: <T>(arg: T) => T. It would be nice, but unfortunately, the current implementation of the type system in TS can propagate constraints only forward, not backward.

@Arnavion
Copy link
Contributor

Expected behavior:
c is of type (x: HTMLElement) => string

Actually you should expect it to be (x: HTMLElement[]) => string

Actual behavior:
c is of type (x: any) => string

Actually it is of type (x: {}) => string


A simpler repro is:

declare function foo<T>(): T;
declare function bar(): string;

declare function baz<T>(f1: () => T, f2: () => T): T;

const quux = baz(foo, bar);

Here, you can expect that since bar is () => string, TS should infer baz's T as string, and thus infer quux as string. Instead, TS infers T as {} and thus quux is also {}

@RyanCavanaugh
Copy link
Member

I disagree that quux should be string. foo has no way of knowing what sort of value to produce, thus we cannot expect its return value to have any specific type. {} is the correct type to infer.

@Arnavion
Copy link
Contributor

(To clarify, I also disagree that quux should be a string without explicitly specializing the baz call. I was just matching the OP to the simpler repro.)

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

mhegazy commented May 18, 2017

related to #15680

@mhegazy
Copy link
Contributor

mhegazy commented May 18, 2017

seems like a duplicate of #9366

@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

5 participants