Description
TypeScript Version: 3.3.0-dev.20181129
Search Terms: ReturnType, Generic Param, Generic Function Argument
Code
//Innocent enough.
//Take a T, return a T
type Foo<T extends number> = (t: T) => T;
//=== NOT using ReturnType<FooT> in Generic Parameters ==
declare function bar<
T extends number,
FooT extends Foo<T>
>(t: T, foo: FooT): ReturnType<FooT>
//OK! `barResult` is of type `3`
const barResult = bar(
3 as (3|5),
() => 3
);
//OK! `barResult2` is of type `3`
const barResult2 = bar(
3 as (3|5),
//`t` is of type `3|5`
//Return type is 3
t => 3
);
//=== Using ReturnType<FooT> in Generic Parameters ==
declare function baz<
T extends number,
FooT extends Foo<T>,
//The same as bar<> but we reference ReturnType<FooT> here
Ret extends ReturnType<FooT>
>(t: T, foo: FooT, ret: Ret): ReturnType<FooT>
//OK! `bazResult` is of type `3`
const bazResult = baz(
3 as (3|5),
() => 3,
3
);
//NOT OK! `bazResult2` is of type `3|5`!
const bazResult2 = baz(
3 as (3|5),
//`t` is of type `3|5`
//Return type is `3`
t => 3,
3
);
//NOT OK! `bazResult3` is of type `3|5`!
const bazResult3 = baz(
3 as (3|5),
//`t` is of type `3|5`
//Return type is `3`
//We *force* the return type to be 3
(t) : 3 => (3 as 3),
3
);
Expected behavior:
bazResult2
and bazResult3
should be of type 3
, the same as bazResult
Actual behavior:
bazResult2
and bazResult3
are of type 3|5
, different from bazResult
(which is of type 3
)
The difference between bazResult
and bazResult2
is literally 3 characters but it causes the inferred types to be very different.
Playground Link: Here
I don't even know how to describe this...
My real use case is more complicated than the above example but I need the return type of a generic function to be inferred correctly to perform stronger compile-time type checks.
The strange thing is that () => 3
will give me the correct return type, but t => 3
will not.
In my own code, using the argument t
is absolutely necessary.
As far as I've seen, this problem only occurs both the following are true,
ReturnType<FooT>
is used in a generic param constraint- The concrete type of
FooT
has parameters (Liket => 3
has parameters,() => 3
does not)
If either of the above are false, this problem does not occur