Typescript: Generic type argument cannot be inferred by a callback parameter return type if the same type was used for the callback's parameter

Created on 13 Jul 2016  路  7Comments  路  Source: microsoft/TypeScript

TypeScript Version: 1.8.10

Code

declare function a<T>(f: (x: T) => T): T;       // callback takes and returns the same generic type
declare function b<T>(f: (x: number) => T): T;  // callback returns generic type, but doesn't take it 

interface X { x: number; }

a<X>(x => ({ x: 1 }));      // ok, inferring isn't needed
a((x: X) => ({ x: 1 }));    // ok, inferred by argument type
a(x => ({ x: 1 }));         // infers T as {}

// on the other hand
b(x => ({ x: 1 }));         // infers T as X

Expected behavior:
In a(x => ({ x: 1 })), T is inferred as { x: number }

Actual behavior:
In a(x => ({ x: 1 })), T is inferred as { }

Needs Proposal Suggestion

Most helpful comment

@RyanCavanaugh

Just imagine how convenient it was to write this (for example - Promise executor):

const promise = new Promise((resolve, reject) => {
    resolve(123);
});
promise.then((res) => {
    console.log('I get called:', res.toFixed());
});

Also disappear errors like this:

const promise = new Promise((resolve, reject) => {
    resolve(123);
    resolve("!");
    resolve(true);
});

But now we have...

const promise = new Promise<number>((resolve, reject) => {
    resolve(123);
});
//or
const promise: Promise<number> = new Promise((resolve, reject) => {
    resolve(123);
});

// more example
function doSome(): Promise<number> {  // let's say that do Some logic is very complicated and we want to explicitly specify the results
    return new Promise<number>((resolve, reject) => { // <number> for greater security logic inside Promise executor
        resolve(123);
    });
}

Many similar problems will go into oblivion if this is done

11701

11537

3038

10785

10609

11058

9660

11537

11877

...and more

All 7 comments

I believe the problem here is we have two inference sites for T -- the return type of the function ({ x: number }), and the type of the parameter (which ends up being { } for lack of better inference), and the most general type among them is then { }.

There might be some fix here without other side effects but I don't know what it would be.

So should we create a proposal to fix that?

It would be very helpful!

@RyanCavanaugh

Just imagine how convenient it was to write this (for example - Promise executor):

const promise = new Promise((resolve, reject) => {
    resolve(123);
});
promise.then((res) => {
    console.log('I get called:', res.toFixed());
});

Also disappear errors like this:

const promise = new Promise((resolve, reject) => {
    resolve(123);
    resolve("!");
    resolve(true);
});

But now we have...

const promise = new Promise<number>((resolve, reject) => {
    resolve(123);
});
//or
const promise: Promise<number> = new Promise((resolve, reject) => {
    resolve(123);
});

// more example
function doSome(): Promise<number> {  // let's say that do Some logic is very complicated and we want to explicitly specify the results
    return new Promise<number>((resolve, reject) => { // <number> for greater security logic inside Promise executor
        resolve(123);
    });
}

Many similar problems will go into oblivion if this is done

11701

11537

3038

10785

10609

11058

9660

11537

11877

...and more

@tsofist I cannot agree regarding #10717, it is not the case for sure

@Igorbek my bad

See this.

type AABB = 'AA' | 'BB';
let arr: any[];
let arr2: AABB[] = arr.map(v => 'AA'); 
let arr3: AABB[] = arr.map(v => 'BB');

If I want it work, I could only use this:

let arr2: AABB[] = arr.map(v => 'AA' as 'AA'); 

'AA' as 'AA' is really unnecessary...

Was this page helpful?
0 / 5 - 0 ratings

Related issues

dlaberge picture dlaberge  路  3Comments

Roam-Cooper picture Roam-Cooper  路  3Comments

blendsdk picture blendsdk  路  3Comments

bgrieder picture bgrieder  路  3Comments

manekinekko picture manekinekko  路  3Comments