Skip to content

Commit afffad4

Browse files
Andaristsandersn
andauthored
Allow non-generic return types to be read from single generic call signatures (#54477)
Co-authored-by: Nathan Shively-Sanders <[email protected]>
1 parent 0e61018 commit afffad4

8 files changed

+441
-8
lines changed

src/compiler/checker.ts

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38870,17 +38870,20 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
3887038870
}
3887138871
}
3887238872

38873-
function getReturnTypeOfSingleNonGenericCallSignature(funcType: Type) {
38873+
function getNonGenericReturnTypeOfSingleCallSignature(funcType: Type) {
3887438874
const signature = getSingleCallSignature(funcType);
38875-
if (signature && !signature.typeParameters) {
38876-
return getReturnTypeOfSignature(signature);
38875+
if (signature) {
38876+
const returnType = getReturnTypeOfSignature(signature);
38877+
if (!signature.typeParameters || !couldContainTypeVariables(returnType)) {
38878+
return returnType;
38879+
}
3887738880
}
3887838881
}
3887938882

3888038883
function getReturnTypeOfSingleNonGenericSignatureOfCallChain(expr: CallChain) {
3888138884
const funcType = checkExpression(expr.expression);
3888238885
const nonOptionalType = getOptionalExpressionType(funcType, expr.expression);
38883-
const returnType = getReturnTypeOfSingleNonGenericCallSignature(funcType);
38886+
const returnType = getNonGenericReturnTypeOfSingleCallSignature(funcType);
3888438887
return returnType && propagateOptionalTypeMarker(returnType, expr, nonOptionalType !== funcType);
3888538888
}
3888638889

@@ -38929,7 +38932,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
3892938932
// signature where we can just fetch the return type without checking the arguments.
3893038933
if (isCallExpression(expr) && expr.expression.kind !== SyntaxKind.SuperKeyword && !isRequireCall(expr, /*requireStringLiteralLikeArgument*/ true) && !isSymbolOrSymbolForCall(expr)) {
3893138934
return isCallChain(expr) ? getReturnTypeOfSingleNonGenericSignatureOfCallChain(expr) :
38932-
getReturnTypeOfSingleNonGenericCallSignature(checkNonNullExpression(expr.expression));
38935+
getNonGenericReturnTypeOfSingleCallSignature(checkNonNullExpression(expr.expression));
3893338936
}
3893438937
else if (isAssertionExpression(expr) && !isConstTypeReference(expr.type)) {
3893538938
return getTypeFromTypeNode((expr as TypeAssertion).type);

tests/baselines/reference/arrayFrom.types

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ const inputALike: ArrayLike<A> = { length: 0 };
3131
const inputARand = getEither(inputA, inputALike);
3232
>inputARand : ArrayLike<A> | Iterable<A>
3333
>getEither(inputA, inputALike) : ArrayLike<A> | Iterable<A>
34-
>getEither : <T>(in1: Iterable<T>, in2: ArrayLike<T>) => ArrayLike<T> | Iterable<T>
34+
>getEither : <T>(in1: Iterable<T>, in2: ArrayLike<T>) => Iterable<T> | ArrayLike<T>
3535
>inputA : A[]
3636
>inputALike : ArrayLike<A>
3737

@@ -163,12 +163,12 @@ const result11: B[] = Array.from(inputASet, ({ a }): B => ({ b: a }));
163163
// the ?: as always taking the false branch, narrowing to ArrayLike<T>,
164164
// even when the type is written as : Iterable<T>|ArrayLike<T>
165165
function getEither<T> (in1: Iterable<T>, in2: ArrayLike<T>) {
166-
>getEither : <T>(in1: Iterable<T>, in2: ArrayLike<T>) => ArrayLike<T> | Iterable<T>
166+
>getEither : <T>(in1: Iterable<T>, in2: ArrayLike<T>) => Iterable<T> | ArrayLike<T>
167167
>in1 : Iterable<T>
168168
>in2 : ArrayLike<T>
169169

170170
return Math.random() > 0.5 ? in1 : in2;
171-
>Math.random() > 0.5 ? in1 : in2 : ArrayLike<T> | Iterable<T>
171+
>Math.random() > 0.5 ? in1 : in2 : Iterable<T> | ArrayLike<T>
172172
>Math.random() > 0.5 : boolean
173173
>Math.random() : number
174174
>Math.random : () => number
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
//// [tests/cases/compiler/circularReferenceInReturnType.ts] ////
2+
3+
=== circularReferenceInReturnType.ts ===
4+
declare function fn1<T>(cb: () => T): string;
5+
>fn1 : Symbol(fn1, Decl(circularReferenceInReturnType.ts, 0, 0))
6+
>T : Symbol(T, Decl(circularReferenceInReturnType.ts, 0, 21))
7+
>cb : Symbol(cb, Decl(circularReferenceInReturnType.ts, 0, 24))
8+
>T : Symbol(T, Decl(circularReferenceInReturnType.ts, 0, 21))
9+
10+
const res1 = fn1(() => res1);
11+
>res1 : Symbol(res1, Decl(circularReferenceInReturnType.ts, 1, 5))
12+
>fn1 : Symbol(fn1, Decl(circularReferenceInReturnType.ts, 0, 0))
13+
>res1 : Symbol(res1, Decl(circularReferenceInReturnType.ts, 1, 5))
14+
15+
declare function fn2<T>(): (cb: () => any) => (a: T) => void;
16+
>fn2 : Symbol(fn2, Decl(circularReferenceInReturnType.ts, 1, 29))
17+
>T : Symbol(T, Decl(circularReferenceInReturnType.ts, 3, 21))
18+
>cb : Symbol(cb, Decl(circularReferenceInReturnType.ts, 3, 28))
19+
>a : Symbol(a, Decl(circularReferenceInReturnType.ts, 3, 47))
20+
>T : Symbol(T, Decl(circularReferenceInReturnType.ts, 3, 21))
21+
22+
const res2 = fn2()(() => res2);
23+
>res2 : Symbol(res2, Decl(circularReferenceInReturnType.ts, 4, 5))
24+
>fn2 : Symbol(fn2, Decl(circularReferenceInReturnType.ts, 1, 29))
25+
>res2 : Symbol(res2, Decl(circularReferenceInReturnType.ts, 4, 5))
26+
27+
declare function fn3<T>(): <T2>(cb: (arg: T2) => any) => (a: T) => void;
28+
>fn3 : Symbol(fn3, Decl(circularReferenceInReturnType.ts, 4, 31))
29+
>T : Symbol(T, Decl(circularReferenceInReturnType.ts, 6, 21))
30+
>T2 : Symbol(T2, Decl(circularReferenceInReturnType.ts, 6, 28))
31+
>cb : Symbol(cb, Decl(circularReferenceInReturnType.ts, 6, 32))
32+
>arg : Symbol(arg, Decl(circularReferenceInReturnType.ts, 6, 37))
33+
>T2 : Symbol(T2, Decl(circularReferenceInReturnType.ts, 6, 28))
34+
>a : Symbol(a, Decl(circularReferenceInReturnType.ts, 6, 58))
35+
>T : Symbol(T, Decl(circularReferenceInReturnType.ts, 6, 21))
36+
37+
const res3 = fn3()(() => res3);
38+
>res3 : Symbol(res3, Decl(circularReferenceInReturnType.ts, 7, 5))
39+
>fn3 : Symbol(fn3, Decl(circularReferenceInReturnType.ts, 4, 31))
40+
>res3 : Symbol(res3, Decl(circularReferenceInReturnType.ts, 7, 5))
41+
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
//// [tests/cases/compiler/circularReferenceInReturnType.ts] ////
2+
3+
=== circularReferenceInReturnType.ts ===
4+
declare function fn1<T>(cb: () => T): string;
5+
>fn1 : <T>(cb: () => T) => string
6+
>cb : () => T
7+
8+
const res1 = fn1(() => res1);
9+
>res1 : string
10+
>fn1(() => res1) : string
11+
>fn1 : <T>(cb: () => T) => string
12+
>() => res1 : () => string
13+
>res1 : string
14+
15+
declare function fn2<T>(): (cb: () => any) => (a: T) => void;
16+
>fn2 : <T>() => (cb: () => any) => (a: T) => void
17+
>cb : () => any
18+
>a : T
19+
20+
const res2 = fn2()(() => res2);
21+
>res2 : (a: unknown) => void
22+
>fn2()(() => res2) : (a: unknown) => void
23+
>fn2() : (cb: () => any) => (a: unknown) => void
24+
>fn2 : <T>() => (cb: () => any) => (a: T) => void
25+
>() => res2 : () => (a: unknown) => void
26+
>res2 : (a: unknown) => void
27+
28+
declare function fn3<T>(): <T2>(cb: (arg: T2) => any) => (a: T) => void;
29+
>fn3 : <T>() => <T2>(cb: (arg: T2) => any) => (a: T) => void
30+
>cb : (arg: T2) => any
31+
>arg : T2
32+
>a : T
33+
34+
const res3 = fn3()(() => res3);
35+
>res3 : (a: unknown) => void
36+
>fn3()(() => res3) : (a: unknown) => void
37+
>fn3() : <T2>(cb: (arg: T2) => any) => (a: unknown) => void
38+
>fn3 : <T>() => <T2>(cb: (arg: T2) => any) => (a: T) => void
39+
>() => res3 : () => (a: unknown) => void
40+
>res3 : (a: unknown) => void
41+
Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
//// [tests/cases/compiler/circularReferenceInReturnType2.ts] ////
2+
3+
=== circularReferenceInReturnType2.ts ===
4+
type ObjectType<Source> = {
5+
>ObjectType : Symbol(ObjectType, Decl(circularReferenceInReturnType2.ts, 0, 0))
6+
>Source : Symbol(Source, Decl(circularReferenceInReturnType2.ts, 0, 16))
7+
8+
kind: "object";
9+
>kind : Symbol(kind, Decl(circularReferenceInReturnType2.ts, 0, 27))
10+
11+
__source: (source: Source) => void;
12+
>__source : Symbol(__source, Decl(circularReferenceInReturnType2.ts, 1, 17))
13+
>source : Symbol(source, Decl(circularReferenceInReturnType2.ts, 2, 13))
14+
>Source : Symbol(Source, Decl(circularReferenceInReturnType2.ts, 0, 16))
15+
16+
};
17+
18+
type Field<Source, Key extends string> = {
19+
>Field : Symbol(Field, Decl(circularReferenceInReturnType2.ts, 3, 2))
20+
>Source : Symbol(Source, Decl(circularReferenceInReturnType2.ts, 5, 11))
21+
>Key : Symbol(Key, Decl(circularReferenceInReturnType2.ts, 5, 18))
22+
23+
__key: (key: Key) => void;
24+
>__key : Symbol(__key, Decl(circularReferenceInReturnType2.ts, 5, 42))
25+
>key : Symbol(key, Decl(circularReferenceInReturnType2.ts, 6, 10))
26+
>Key : Symbol(Key, Decl(circularReferenceInReturnType2.ts, 5, 18))
27+
28+
__source: (source: Source) => void;
29+
>__source : Symbol(__source, Decl(circularReferenceInReturnType2.ts, 6, 28))
30+
>source : Symbol(source, Decl(circularReferenceInReturnType2.ts, 7, 13))
31+
>Source : Symbol(Source, Decl(circularReferenceInReturnType2.ts, 5, 11))
32+
33+
};
34+
35+
declare const object: <Source>() => <
36+
>object : Symbol(object, Decl(circularReferenceInReturnType2.ts, 10, 13))
37+
>Source : Symbol(Source, Decl(circularReferenceInReturnType2.ts, 10, 23))
38+
39+
Fields extends {
40+
>Fields : Symbol(Fields, Decl(circularReferenceInReturnType2.ts, 10, 37))
41+
42+
[Key in keyof Fields]: Field<Source, Key & string>;
43+
>Key : Symbol(Key, Decl(circularReferenceInReturnType2.ts, 12, 5))
44+
>Fields : Symbol(Fields, Decl(circularReferenceInReturnType2.ts, 10, 37))
45+
>Field : Symbol(Field, Decl(circularReferenceInReturnType2.ts, 3, 2))
46+
>Source : Symbol(Source, Decl(circularReferenceInReturnType2.ts, 10, 23))
47+
>Key : Symbol(Key, Decl(circularReferenceInReturnType2.ts, 12, 5))
48+
}
49+
>(config: {
50+
>config : Symbol(config, Decl(circularReferenceInReturnType2.ts, 14, 2))
51+
52+
name: string;
53+
>name : Symbol(name, Decl(circularReferenceInReturnType2.ts, 14, 11))
54+
55+
fields: Fields | (() => Fields);
56+
>fields : Symbol(fields, Decl(circularReferenceInReturnType2.ts, 15, 15))
57+
>Fields : Symbol(Fields, Decl(circularReferenceInReturnType2.ts, 10, 37))
58+
>Fields : Symbol(Fields, Decl(circularReferenceInReturnType2.ts, 10, 37))
59+
60+
}) => ObjectType<Source>;
61+
>ObjectType : Symbol(ObjectType, Decl(circularReferenceInReturnType2.ts, 0, 0))
62+
>Source : Symbol(Source, Decl(circularReferenceInReturnType2.ts, 10, 23))
63+
64+
type InferValueFromObjectType<Type extends ObjectType<any>> =
65+
>InferValueFromObjectType : Symbol(InferValueFromObjectType, Decl(circularReferenceInReturnType2.ts, 17, 25))
66+
>Type : Symbol(Type, Decl(circularReferenceInReturnType2.ts, 19, 30))
67+
>ObjectType : Symbol(ObjectType, Decl(circularReferenceInReturnType2.ts, 0, 0))
68+
69+
Type extends ObjectType<infer Source> ? Source : never;
70+
>Type : Symbol(Type, Decl(circularReferenceInReturnType2.ts, 19, 30))
71+
>ObjectType : Symbol(ObjectType, Decl(circularReferenceInReturnType2.ts, 0, 0))
72+
>Source : Symbol(Source, Decl(circularReferenceInReturnType2.ts, 20, 31))
73+
>Source : Symbol(Source, Decl(circularReferenceInReturnType2.ts, 20, 31))
74+
75+
type FieldResolver<Source, TType extends ObjectType<any>> = (
76+
>FieldResolver : Symbol(FieldResolver, Decl(circularReferenceInReturnType2.ts, 20, 57))
77+
>Source : Symbol(Source, Decl(circularReferenceInReturnType2.ts, 22, 19))
78+
>TType : Symbol(TType, Decl(circularReferenceInReturnType2.ts, 22, 26))
79+
>ObjectType : Symbol(ObjectType, Decl(circularReferenceInReturnType2.ts, 0, 0))
80+
81+
source: Source
82+
>source : Symbol(source, Decl(circularReferenceInReturnType2.ts, 22, 61))
83+
>Source : Symbol(Source, Decl(circularReferenceInReturnType2.ts, 22, 19))
84+
85+
) => InferValueFromObjectType<TType>;
86+
>InferValueFromObjectType : Symbol(InferValueFromObjectType, Decl(circularReferenceInReturnType2.ts, 17, 25))
87+
>TType : Symbol(TType, Decl(circularReferenceInReturnType2.ts, 22, 26))
88+
89+
type FieldFuncArgs<Source, Type extends ObjectType<any>> = {
90+
>FieldFuncArgs : Symbol(FieldFuncArgs, Decl(circularReferenceInReturnType2.ts, 24, 37))
91+
>Source : Symbol(Source, Decl(circularReferenceInReturnType2.ts, 26, 19))
92+
>Type : Symbol(Type, Decl(circularReferenceInReturnType2.ts, 26, 26))
93+
>ObjectType : Symbol(ObjectType, Decl(circularReferenceInReturnType2.ts, 0, 0))
94+
95+
type: Type;
96+
>type : Symbol(type, Decl(circularReferenceInReturnType2.ts, 26, 60))
97+
>Type : Symbol(Type, Decl(circularReferenceInReturnType2.ts, 26, 26))
98+
99+
resolve: FieldResolver<Source, Type>;
100+
>resolve : Symbol(resolve, Decl(circularReferenceInReturnType2.ts, 27, 13))
101+
>FieldResolver : Symbol(FieldResolver, Decl(circularReferenceInReturnType2.ts, 20, 57))
102+
>Source : Symbol(Source, Decl(circularReferenceInReturnType2.ts, 26, 19))
103+
>Type : Symbol(Type, Decl(circularReferenceInReturnType2.ts, 26, 26))
104+
105+
};
106+
107+
declare const field: <Source, Type extends ObjectType<any>, Key extends string>(
108+
>field : Symbol(field, Decl(circularReferenceInReturnType2.ts, 31, 13))
109+
>Source : Symbol(Source, Decl(circularReferenceInReturnType2.ts, 31, 22))
110+
>Type : Symbol(Type, Decl(circularReferenceInReturnType2.ts, 31, 29))
111+
>ObjectType : Symbol(ObjectType, Decl(circularReferenceInReturnType2.ts, 0, 0))
112+
>Key : Symbol(Key, Decl(circularReferenceInReturnType2.ts, 31, 59))
113+
114+
field: FieldFuncArgs<Source, Type>
115+
>field : Symbol(field, Decl(circularReferenceInReturnType2.ts, 31, 80))
116+
>FieldFuncArgs : Symbol(FieldFuncArgs, Decl(circularReferenceInReturnType2.ts, 24, 37))
117+
>Source : Symbol(Source, Decl(circularReferenceInReturnType2.ts, 31, 22))
118+
>Type : Symbol(Type, Decl(circularReferenceInReturnType2.ts, 31, 29))
119+
120+
) => Field<Source, Key>;
121+
>Field : Symbol(Field, Decl(circularReferenceInReturnType2.ts, 3, 2))
122+
>Source : Symbol(Source, Decl(circularReferenceInReturnType2.ts, 31, 22))
123+
>Key : Symbol(Key, Decl(circularReferenceInReturnType2.ts, 31, 59))
124+
125+
type Something = { foo: number };
126+
>Something : Symbol(Something, Decl(circularReferenceInReturnType2.ts, 33, 24))
127+
>foo : Symbol(foo, Decl(circularReferenceInReturnType2.ts, 35, 18))
128+
129+
const A = object<Something>()({
130+
>A : Symbol(A, Decl(circularReferenceInReturnType2.ts, 37, 5))
131+
>object : Symbol(object, Decl(circularReferenceInReturnType2.ts, 10, 13))
132+
>Something : Symbol(Something, Decl(circularReferenceInReturnType2.ts, 33, 24))
133+
134+
name: "A",
135+
>name : Symbol(name, Decl(circularReferenceInReturnType2.ts, 37, 31))
136+
137+
fields: () => ({
138+
>fields : Symbol(fields, Decl(circularReferenceInReturnType2.ts, 38, 12))
139+
140+
a: field({
141+
>a : Symbol(a, Decl(circularReferenceInReturnType2.ts, 39, 18))
142+
>field : Symbol(field, Decl(circularReferenceInReturnType2.ts, 31, 13))
143+
144+
type: A,
145+
>type : Symbol(type, Decl(circularReferenceInReturnType2.ts, 40, 14))
146+
>A : Symbol(A, Decl(circularReferenceInReturnType2.ts, 37, 5))
147+
148+
resolve() {
149+
>resolve : Symbol(resolve, Decl(circularReferenceInReturnType2.ts, 41, 14))
150+
151+
return {
152+
foo: 100,
153+
>foo : Symbol(foo, Decl(circularReferenceInReturnType2.ts, 43, 16))
154+
155+
};
156+
},
157+
}),
158+
}),
159+
});
160+

0 commit comments

Comments
 (0)