Skip to content

Commit e1874f3

Browse files
authored
Propagate outer type parameters of single signature types (#57403)
1 parent 6d0cc1b commit e1874f3

11 files changed

+423
-17
lines changed

src/compiler/checker.ts

+67-17
Large diffs are not rendered by default.

src/compiler/types.ts

+9
Original file line numberDiff line numberDiff line change
@@ -6221,6 +6221,7 @@ export const enum ObjectFlags {
62216221
ContainsSpread = 1 << 21, // Object literal contains spread operation
62226222
ObjectRestType = 1 << 22, // Originates in object rest declaration
62236223
InstantiationExpressionType = 1 << 23, // Originates in instantiation expression
6224+
SingleSignatureType = 1 << 27, // A single signature type extracted from a potentially broader type
62246225
/** @internal */
62256226
IsClassInstanceClone = 1 << 24, // Type is a clone of a class instance type
62266227
// Flags that require TypeFlags.Object and ObjectFlags.Reference
@@ -6431,6 +6432,12 @@ export interface AnonymousType extends ObjectType {
64316432
instantiations?: Map<string, Type>; // Instantiations of generic type alias (undefined if non-generic)
64326433
}
64336434

6435+
/** @internal */
6436+
// A SingleSignatureType may have bespoke outer type parameters to handle free type variable inferences
6437+
export interface SingleSignatureType extends AnonymousType {
6438+
outerTypeParameters?: TypeParameter[];
6439+
}
6440+
64346441
/** @internal */
64356442
export interface InstantiationExpressionType extends AnonymousType {
64366443
node: NodeWithTypeArguments;
@@ -6716,6 +6723,8 @@ export interface Signature {
67166723
isolatedSignatureType?: ObjectType; // A manufactured type that just contains the signature for purposes of signature comparison
67176724
/** @internal */
67186725
instantiations?: Map<string, Signature>; // Generic signature instantiation cache
6726+
/** @internal */
6727+
implementationSignatureCache?: Signature; // Copy of the signature with fresh type parameters to use in checking the body of a potentially self-referential generic function (deferred)
67196728
}
67206729

67216730
export const enum IndexKind {

tests/baselines/reference/api/typescript.d.ts

+1
Original file line numberDiff line numberDiff line change
@@ -6598,6 +6598,7 @@ declare namespace ts {
65986598
ContainsSpread = 2097152,
65996599
ObjectRestType = 4194304,
66006600
InstantiationExpressionType = 8388608,
6601+
SingleSignatureType = 134217728,
66016602
}
66026603
interface ObjectType extends Type {
66036604
objectFlags: ObjectFlags;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
//// [tests/cases/compiler/genericCallWithinOwnBodyCastTypeParameterIdentity.ts] ////
2+
3+
//// [genericCallWithinOwnBodyCastTypeParameterIdentity.ts]
4+
interface Thenable<Value> {
5+
then<V>(
6+
onFulfilled: (value: Value) => V | Thenable<V>,
7+
): Thenable<V>;
8+
}
9+
10+
const toThenable = <Result, Input>(fn: (input: Input) => Result | Thenable<Result>) =>
11+
(input: Input): Thenable<Result> => {
12+
const result = fn(input)
13+
return {
14+
then<V>(onFulfilled: (value: Result) => V | Thenable<V>) {
15+
return toThenable<V, Result>(onFulfilled)(result as Result)
16+
}
17+
};
18+
}
19+
20+
const toThenableInferred = <Result, Input>(fn: (input: Input) => Result | Thenable<Result>) =>
21+
(input: Input): Thenable<Result> => {
22+
const result = fn(input)
23+
return {
24+
then(onFulfilled) {
25+
return toThenableInferred(onFulfilled)(result as Result)
26+
}
27+
};
28+
}
29+
30+
31+
//// [genericCallWithinOwnBodyCastTypeParameterIdentity.js]
32+
"use strict";
33+
var toThenable = function (fn) {
34+
return function (input) {
35+
var result = fn(input);
36+
return {
37+
then: function (onFulfilled) {
38+
return toThenable(onFulfilled)(result);
39+
}
40+
};
41+
};
42+
};
43+
var toThenableInferred = function (fn) {
44+
return function (input) {
45+
var result = fn(input);
46+
return {
47+
then: function (onFulfilled) {
48+
return toThenableInferred(onFulfilled)(result);
49+
}
50+
};
51+
};
52+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
//// [tests/cases/compiler/genericCallWithinOwnBodyCastTypeParameterIdentity.ts] ////
2+
3+
=== genericCallWithinOwnBodyCastTypeParameterIdentity.ts ===
4+
interface Thenable<Value> {
5+
>Thenable : Symbol(Thenable, Decl(genericCallWithinOwnBodyCastTypeParameterIdentity.ts, 0, 0))
6+
>Value : Symbol(Value, Decl(genericCallWithinOwnBodyCastTypeParameterIdentity.ts, 0, 19))
7+
8+
then<V>(
9+
>then : Symbol(Thenable.then, Decl(genericCallWithinOwnBodyCastTypeParameterIdentity.ts, 0, 27))
10+
>V : Symbol(V, Decl(genericCallWithinOwnBodyCastTypeParameterIdentity.ts, 1, 9))
11+
12+
onFulfilled: (value: Value) => V | Thenable<V>,
13+
>onFulfilled : Symbol(onFulfilled, Decl(genericCallWithinOwnBodyCastTypeParameterIdentity.ts, 1, 12))
14+
>value : Symbol(value, Decl(genericCallWithinOwnBodyCastTypeParameterIdentity.ts, 2, 22))
15+
>Value : Symbol(Value, Decl(genericCallWithinOwnBodyCastTypeParameterIdentity.ts, 0, 19))
16+
>V : Symbol(V, Decl(genericCallWithinOwnBodyCastTypeParameterIdentity.ts, 1, 9))
17+
>Thenable : Symbol(Thenable, Decl(genericCallWithinOwnBodyCastTypeParameterIdentity.ts, 0, 0))
18+
>V : Symbol(V, Decl(genericCallWithinOwnBodyCastTypeParameterIdentity.ts, 1, 9))
19+
20+
): Thenable<V>;
21+
>Thenable : Symbol(Thenable, Decl(genericCallWithinOwnBodyCastTypeParameterIdentity.ts, 0, 0))
22+
>V : Symbol(V, Decl(genericCallWithinOwnBodyCastTypeParameterIdentity.ts, 1, 9))
23+
}
24+
25+
const toThenable = <Result, Input>(fn: (input: Input) => Result | Thenable<Result>) =>
26+
>toThenable : Symbol(toThenable, Decl(genericCallWithinOwnBodyCastTypeParameterIdentity.ts, 6, 5))
27+
>Result : Symbol(Result, Decl(genericCallWithinOwnBodyCastTypeParameterIdentity.ts, 6, 20))
28+
>Input : Symbol(Input, Decl(genericCallWithinOwnBodyCastTypeParameterIdentity.ts, 6, 27))
29+
>fn : Symbol(fn, Decl(genericCallWithinOwnBodyCastTypeParameterIdentity.ts, 6, 35))
30+
>input : Symbol(input, Decl(genericCallWithinOwnBodyCastTypeParameterIdentity.ts, 6, 40))
31+
>Input : Symbol(Input, Decl(genericCallWithinOwnBodyCastTypeParameterIdentity.ts, 6, 27))
32+
>Result : Symbol(Result, Decl(genericCallWithinOwnBodyCastTypeParameterIdentity.ts, 6, 20))
33+
>Thenable : Symbol(Thenable, Decl(genericCallWithinOwnBodyCastTypeParameterIdentity.ts, 0, 0))
34+
>Result : Symbol(Result, Decl(genericCallWithinOwnBodyCastTypeParameterIdentity.ts, 6, 20))
35+
36+
(input: Input): Thenable<Result> => {
37+
>input : Symbol(input, Decl(genericCallWithinOwnBodyCastTypeParameterIdentity.ts, 7, 5))
38+
>Input : Symbol(Input, Decl(genericCallWithinOwnBodyCastTypeParameterIdentity.ts, 6, 27))
39+
>Thenable : Symbol(Thenable, Decl(genericCallWithinOwnBodyCastTypeParameterIdentity.ts, 0, 0))
40+
>Result : Symbol(Result, Decl(genericCallWithinOwnBodyCastTypeParameterIdentity.ts, 6, 20))
41+
42+
const result = fn(input)
43+
>result : Symbol(result, Decl(genericCallWithinOwnBodyCastTypeParameterIdentity.ts, 8, 13))
44+
>fn : Symbol(fn, Decl(genericCallWithinOwnBodyCastTypeParameterIdentity.ts, 6, 35))
45+
>input : Symbol(input, Decl(genericCallWithinOwnBodyCastTypeParameterIdentity.ts, 7, 5))
46+
47+
return {
48+
then<V>(onFulfilled: (value: Result) => V | Thenable<V>) {
49+
>then : Symbol(then, Decl(genericCallWithinOwnBodyCastTypeParameterIdentity.ts, 9, 16))
50+
>V : Symbol(V, Decl(genericCallWithinOwnBodyCastTypeParameterIdentity.ts, 10, 17))
51+
>onFulfilled : Symbol(onFulfilled, Decl(genericCallWithinOwnBodyCastTypeParameterIdentity.ts, 10, 20))
52+
>value : Symbol(value, Decl(genericCallWithinOwnBodyCastTypeParameterIdentity.ts, 10, 34))
53+
>Result : Symbol(Result, Decl(genericCallWithinOwnBodyCastTypeParameterIdentity.ts, 6, 20))
54+
>V : Symbol(V, Decl(genericCallWithinOwnBodyCastTypeParameterIdentity.ts, 10, 17))
55+
>Thenable : Symbol(Thenable, Decl(genericCallWithinOwnBodyCastTypeParameterIdentity.ts, 0, 0))
56+
>V : Symbol(V, Decl(genericCallWithinOwnBodyCastTypeParameterIdentity.ts, 10, 17))
57+
58+
return toThenable<V, Result>(onFulfilled)(result as Result)
59+
>toThenable : Symbol(toThenable, Decl(genericCallWithinOwnBodyCastTypeParameterIdentity.ts, 6, 5))
60+
>V : Symbol(V, Decl(genericCallWithinOwnBodyCastTypeParameterIdentity.ts, 10, 17))
61+
>Result : Symbol(Result, Decl(genericCallWithinOwnBodyCastTypeParameterIdentity.ts, 6, 20))
62+
>onFulfilled : Symbol(onFulfilled, Decl(genericCallWithinOwnBodyCastTypeParameterIdentity.ts, 10, 20))
63+
>result : Symbol(result, Decl(genericCallWithinOwnBodyCastTypeParameterIdentity.ts, 8, 13))
64+
>Result : Symbol(Result, Decl(genericCallWithinOwnBodyCastTypeParameterIdentity.ts, 6, 20))
65+
}
66+
};
67+
}
68+
69+
const toThenableInferred = <Result, Input>(fn: (input: Input) => Result | Thenable<Result>) =>
70+
>toThenableInferred : Symbol(toThenableInferred, Decl(genericCallWithinOwnBodyCastTypeParameterIdentity.ts, 16, 5))
71+
>Result : Symbol(Result, Decl(genericCallWithinOwnBodyCastTypeParameterIdentity.ts, 16, 28))
72+
>Input : Symbol(Input, Decl(genericCallWithinOwnBodyCastTypeParameterIdentity.ts, 16, 35))
73+
>fn : Symbol(fn, Decl(genericCallWithinOwnBodyCastTypeParameterIdentity.ts, 16, 43))
74+
>input : Symbol(input, Decl(genericCallWithinOwnBodyCastTypeParameterIdentity.ts, 16, 48))
75+
>Input : Symbol(Input, Decl(genericCallWithinOwnBodyCastTypeParameterIdentity.ts, 16, 35))
76+
>Result : Symbol(Result, Decl(genericCallWithinOwnBodyCastTypeParameterIdentity.ts, 16, 28))
77+
>Thenable : Symbol(Thenable, Decl(genericCallWithinOwnBodyCastTypeParameterIdentity.ts, 0, 0))
78+
>Result : Symbol(Result, Decl(genericCallWithinOwnBodyCastTypeParameterIdentity.ts, 16, 28))
79+
80+
(input: Input): Thenable<Result> => {
81+
>input : Symbol(input, Decl(genericCallWithinOwnBodyCastTypeParameterIdentity.ts, 17, 5))
82+
>Input : Symbol(Input, Decl(genericCallWithinOwnBodyCastTypeParameterIdentity.ts, 16, 35))
83+
>Thenable : Symbol(Thenable, Decl(genericCallWithinOwnBodyCastTypeParameterIdentity.ts, 0, 0))
84+
>Result : Symbol(Result, Decl(genericCallWithinOwnBodyCastTypeParameterIdentity.ts, 16, 28))
85+
86+
const result = fn(input)
87+
>result : Symbol(result, Decl(genericCallWithinOwnBodyCastTypeParameterIdentity.ts, 18, 13))
88+
>fn : Symbol(fn, Decl(genericCallWithinOwnBodyCastTypeParameterIdentity.ts, 16, 43))
89+
>input : Symbol(input, Decl(genericCallWithinOwnBodyCastTypeParameterIdentity.ts, 17, 5))
90+
91+
return {
92+
then(onFulfilled) {
93+
>then : Symbol(then, Decl(genericCallWithinOwnBodyCastTypeParameterIdentity.ts, 19, 16))
94+
>onFulfilled : Symbol(onFulfilled, Decl(genericCallWithinOwnBodyCastTypeParameterIdentity.ts, 20, 17))
95+
96+
return toThenableInferred(onFulfilled)(result as Result)
97+
>toThenableInferred : Symbol(toThenableInferred, Decl(genericCallWithinOwnBodyCastTypeParameterIdentity.ts, 16, 5))
98+
>onFulfilled : Symbol(onFulfilled, Decl(genericCallWithinOwnBodyCastTypeParameterIdentity.ts, 20, 17))
99+
>result : Symbol(result, Decl(genericCallWithinOwnBodyCastTypeParameterIdentity.ts, 18, 13))
100+
>Result : Symbol(Result, Decl(genericCallWithinOwnBodyCastTypeParameterIdentity.ts, 16, 28))
101+
}
102+
};
103+
}
104+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
//// [tests/cases/compiler/genericCallWithinOwnBodyCastTypeParameterIdentity.ts] ////
2+
3+
=== genericCallWithinOwnBodyCastTypeParameterIdentity.ts ===
4+
interface Thenable<Value> {
5+
then<V>(
6+
>then : <V>(onFulfilled: (value: Value) => V | Thenable<V>) => Thenable<V>
7+
8+
onFulfilled: (value: Value) => V | Thenable<V>,
9+
>onFulfilled : (value: Value) => V | Thenable<V>
10+
>value : Value
11+
12+
): Thenable<V>;
13+
}
14+
15+
const toThenable = <Result, Input>(fn: (input: Input) => Result | Thenable<Result>) =>
16+
>toThenable : <Result, Input>(fn: (input: Input) => Result | Thenable<Result>) => (input: Input) => Thenable<Result>
17+
><Result, Input>(fn: (input: Input) => Result | Thenable<Result>) => (input: Input): Thenable<Result> => { const result = fn(input) return { then<V>(onFulfilled: (value: Result) => V | Thenable<V>) { return toThenable<V, Result>(onFulfilled)(result as Result) } }; } : <Result, Input>(fn: (input: Input) => Result | Thenable<Result>) => (input: Input) => Thenable<Result>
18+
>fn : (input: Input) => Result | Thenable<Result>
19+
>input : Input
20+
21+
(input: Input): Thenable<Result> => {
22+
>(input: Input): Thenable<Result> => { const result = fn(input) return { then<V>(onFulfilled: (value: Result) => V | Thenable<V>) { return toThenable<V, Result>(onFulfilled)(result as Result) } }; } : (input: Input) => Thenable<Result>
23+
>input : Input
24+
25+
const result = fn(input)
26+
>result : Result | Thenable<Result>
27+
>fn(input) : Result | Thenable<Result>
28+
>fn : (input: Input) => Result | Thenable<Result>
29+
>input : Input
30+
31+
return {
32+
>{ then<V>(onFulfilled: (value: Result) => V | Thenable<V>) { return toThenable<V, Result>(onFulfilled)(result as Result) } } : { then<V>(onFulfilled: (value: Result) => V | Thenable<V>): Thenable<V>; }
33+
34+
then<V>(onFulfilled: (value: Result) => V | Thenable<V>) {
35+
>then : <V>(onFulfilled: (value: Result) => V | Thenable<V>) => Thenable<V>
36+
>onFulfilled : (value: Result) => V | Thenable<V>
37+
>value : Result
38+
39+
return toThenable<V, Result>(onFulfilled)(result as Result)
40+
>toThenable<V, Result>(onFulfilled)(result as Result) : Thenable<V>
41+
>toThenable<V, Result>(onFulfilled) : (input: Result) => Thenable<V>
42+
>toThenable : <Result, Input>(fn: (input: Input) => Result | Thenable<Result>) => (input: Input) => Thenable<Result>
43+
>onFulfilled : (value: Result) => V | Thenable<V>
44+
>result as Result : Result
45+
>result : Result | Thenable<Result>
46+
}
47+
};
48+
}
49+
50+
const toThenableInferred = <Result, Input>(fn: (input: Input) => Result | Thenable<Result>) =>
51+
>toThenableInferred : <Result, Input>(fn: (input: Input) => Result | Thenable<Result>) => (input: Input) => Thenable<Result>
52+
><Result, Input>(fn: (input: Input) => Result | Thenable<Result>) => (input: Input): Thenable<Result> => { const result = fn(input) return { then(onFulfilled) { return toThenableInferred(onFulfilled)(result as Result) } }; } : <Result, Input>(fn: (input: Input) => Result | Thenable<Result>) => (input: Input) => Thenable<Result>
53+
>fn : (input: Input) => Result | Thenable<Result>
54+
>input : Input
55+
56+
(input: Input): Thenable<Result> => {
57+
>(input: Input): Thenable<Result> => { const result = fn(input) return { then(onFulfilled) { return toThenableInferred(onFulfilled)(result as Result) } }; } : (input: Input) => Thenable<Result>
58+
>input : Input
59+
60+
const result = fn(input)
61+
>result : Result | Thenable<Result>
62+
>fn(input) : Result | Thenable<Result>
63+
>fn : (input: Input) => Result | Thenable<Result>
64+
>input : Input
65+
66+
return {
67+
>{ then(onFulfilled) { return toThenableInferred(onFulfilled)(result as Result) } } : { then<V>(onFulfilled: (value: Result) => V | Thenable<V>): Thenable<V>; }
68+
69+
then(onFulfilled) {
70+
>then : <V>(onFulfilled: (value: Result) => V | Thenable<V>) => Thenable<V>
71+
>onFulfilled : (value: Result) => V | Thenable<V>
72+
73+
return toThenableInferred(onFulfilled)(result as Result)
74+
>toThenableInferred(onFulfilled)(result as Result) : Thenable<V>
75+
>toThenableInferred(onFulfilled) : (input: Result) => Thenable<V>
76+
>toThenableInferred : <Result, Input>(fn: (input: Input) => Result | Thenable<Result>) => (input: Input) => Thenable<Result>
77+
>onFulfilled : (value: Result) => V | Thenable<V>
78+
>result as Result : Result
79+
>result : Result | Thenable<Result>
80+
}
81+
};
82+
}
83+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
//// [tests/cases/compiler/nestedGenericSpreadInference.ts] ////
2+
3+
//// [nestedGenericSpreadInference.ts]
4+
declare function wrap<X>(x: X): { x: X };
5+
declare function call<A extends unknown[], T>(x: { x: (...args: A) => T }, ...args: A): T;
6+
7+
// This should be of type `number` - ideally, it also would not error.
8+
const leak = call(wrap(<T>(x: T) => x), 1);
9+
10+
11+
//// [nestedGenericSpreadInference.js]
12+
"use strict";
13+
// This should be of type `number` - ideally, it also would not error.
14+
var leak = call(wrap(function (x) { return x; }), 1);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
//// [tests/cases/compiler/nestedGenericSpreadInference.ts] ////
2+
3+
=== nestedGenericSpreadInference.ts ===
4+
declare function wrap<X>(x: X): { x: X };
5+
>wrap : Symbol(wrap, Decl(nestedGenericSpreadInference.ts, 0, 0))
6+
>X : Symbol(X, Decl(nestedGenericSpreadInference.ts, 0, 22))
7+
>x : Symbol(x, Decl(nestedGenericSpreadInference.ts, 0, 25))
8+
>X : Symbol(X, Decl(nestedGenericSpreadInference.ts, 0, 22))
9+
>x : Symbol(x, Decl(nestedGenericSpreadInference.ts, 0, 33))
10+
>X : Symbol(X, Decl(nestedGenericSpreadInference.ts, 0, 22))
11+
12+
declare function call<A extends unknown[], T>(x: { x: (...args: A) => T }, ...args: A): T;
13+
>call : Symbol(call, Decl(nestedGenericSpreadInference.ts, 0, 41))
14+
>A : Symbol(A, Decl(nestedGenericSpreadInference.ts, 1, 22))
15+
>T : Symbol(T, Decl(nestedGenericSpreadInference.ts, 1, 42))
16+
>x : Symbol(x, Decl(nestedGenericSpreadInference.ts, 1, 46))
17+
>x : Symbol(x, Decl(nestedGenericSpreadInference.ts, 1, 50))
18+
>args : Symbol(args, Decl(nestedGenericSpreadInference.ts, 1, 55))
19+
>A : Symbol(A, Decl(nestedGenericSpreadInference.ts, 1, 22))
20+
>T : Symbol(T, Decl(nestedGenericSpreadInference.ts, 1, 42))
21+
>args : Symbol(args, Decl(nestedGenericSpreadInference.ts, 1, 74))
22+
>A : Symbol(A, Decl(nestedGenericSpreadInference.ts, 1, 22))
23+
>T : Symbol(T, Decl(nestedGenericSpreadInference.ts, 1, 42))
24+
25+
// This should be of type `number` - ideally, it also would not error.
26+
const leak = call(wrap(<T>(x: T) => x), 1);
27+
>leak : Symbol(leak, Decl(nestedGenericSpreadInference.ts, 4, 5))
28+
>call : Symbol(call, Decl(nestedGenericSpreadInference.ts, 0, 41))
29+
>wrap : Symbol(wrap, Decl(nestedGenericSpreadInference.ts, 0, 0))
30+
>T : Symbol(T, Decl(nestedGenericSpreadInference.ts, 4, 24))
31+
>x : Symbol(x, Decl(nestedGenericSpreadInference.ts, 4, 27))
32+
>T : Symbol(T, Decl(nestedGenericSpreadInference.ts, 4, 24))
33+
>x : Symbol(x, Decl(nestedGenericSpreadInference.ts, 4, 27))
34+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
//// [tests/cases/compiler/nestedGenericSpreadInference.ts] ////
2+
3+
=== nestedGenericSpreadInference.ts ===
4+
declare function wrap<X>(x: X): { x: X };
5+
>wrap : <X>(x: X) => { x: X;}
6+
>x : X
7+
>x : X
8+
9+
declare function call<A extends unknown[], T>(x: { x: (...args: A) => T }, ...args: A): T;
10+
>call : <A extends unknown[], T>(x: { x: (...args: A) => T; }, ...args: A) => T
11+
>x : { x: (...args: A) => T; }
12+
>x : (...args: A) => T
13+
>args : A
14+
>args : A
15+
16+
// This should be of type `number` - ideally, it also would not error.
17+
const leak = call(wrap(<T>(x: T) => x), 1);
18+
>leak : number
19+
>call(wrap(<T>(x: T) => x), 1) : number
20+
>call : <A extends unknown[], T>(x: { x: (...args: A) => T; }, ...args: A) => T
21+
>wrap(<T>(x: T) => x) : { x: (x: A[0]) => A[0]; }
22+
>wrap : <X>(x: X) => { x: X; }
23+
><T>(x: T) => x : <T>(x: T) => T
24+
>x : T
25+
>x : T
26+
>1 : 1
27+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// @strict: true
2+
interface Thenable<Value> {
3+
then<V>(
4+
onFulfilled: (value: Value) => V | Thenable<V>,
5+
): Thenable<V>;
6+
}
7+
8+
const toThenable = <Result, Input>(fn: (input: Input) => Result | Thenable<Result>) =>
9+
(input: Input): Thenable<Result> => {
10+
const result = fn(input)
11+
return {
12+
then<V>(onFulfilled: (value: Result) => V | Thenable<V>) {
13+
return toThenable<V, Result>(onFulfilled)(result as Result)
14+
}
15+
};
16+
}
17+
18+
const toThenableInferred = <Result, Input>(fn: (input: Input) => Result | Thenable<Result>) =>
19+
(input: Input): Thenable<Result> => {
20+
const result = fn(input)
21+
return {
22+
then(onFulfilled) {
23+
return toThenableInferred(onFulfilled)(result as Result)
24+
}
25+
};
26+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
// @strict: true
2+
declare function wrap<X>(x: X): { x: X };
3+
declare function call<A extends unknown[], T>(x: { x: (...args: A) => T }, ...args: A): T;
4+
5+
// This should be of type `number` - ideally, it also would not error.
6+
const leak = call(wrap(<T>(x: T) => x), 1);

0 commit comments

Comments
 (0)