Skip to content

Commit 10bbe3d

Browse files
committed
Revert microsoft#54477 but keep the tests
Discovered in microsoft#57117 The implementation should not `couldContainTypeVariables`--it's not intended a fast path, and should not be used in places where its unreliability can be observed. The tests stay, but with a note added that they should pass but do not.
1 parent 94f4379 commit 10bbe3d

10 files changed

+133
-53
lines changed

src/compiler/checker.ts

+5-8
Original file line numberDiff line numberDiff line change
@@ -39347,20 +39347,17 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
3934739347
}
3934839348
}
3934939349

39350-
function getNonGenericReturnTypeOfSingleCallSignature(funcType: Type) {
39350+
function getReturnTypeOfSingleNonGenericCallSignature(funcType: Type) {
3935139351
const signature = getSingleCallSignature(funcType);
39352-
if (signature) {
39353-
const returnType = getReturnTypeOfSignature(signature);
39354-
if (!signature.typeParameters || !couldContainTypeVariables(returnType)) {
39355-
return returnType;
39356-
}
39352+
if (signature && !signature.typeParameters) {
39353+
return getReturnTypeOfSignature(signature);
3935739354
}
3935839355
}
3935939356

3936039357
function getReturnTypeOfSingleNonGenericSignatureOfCallChain(expr: CallChain) {
3936139358
const funcType = checkExpression(expr.expression);
3936239359
const nonOptionalType = getOptionalExpressionType(funcType, expr.expression);
39363-
const returnType = getNonGenericReturnTypeOfSingleCallSignature(funcType);
39360+
const returnType = getReturnTypeOfSingleNonGenericCallSignature(funcType);
3936439361
return returnType && propagateOptionalTypeMarker(returnType, expr, nonOptionalType !== funcType);
3936539362
}
3936639363

@@ -39409,7 +39406,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
3940939406
// signature where we can just fetch the return type without checking the arguments.
3941039407
if (isCallExpression(expr) && expr.expression.kind !== SyntaxKind.SuperKeyword && !isRequireCall(expr, /*requireStringLiteralLikeArgument*/ true) && !isSymbolOrSymbolForCall(expr)) {
3941139408
return isCallChain(expr) ? getReturnTypeOfSingleNonGenericSignatureOfCallChain(expr) :
39412-
getNonGenericReturnTypeOfSingleCallSignature(checkNonNullExpression(expr.expression));
39409+
getReturnTypeOfSingleNonGenericCallSignature(checkNonNullExpression(expr.expression));
3941339410
}
3941439411
else if (isAssertionExpression(expr) && !isConstTypeReference(expr.type)) {
3941539412
return getTypeFromTypeNode((expr as TypeAssertion).type);

tests/baselines/reference/arrayFrom.types

+3-3
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>) => Iterable<T> | ArrayLike<T>
34+
>getEither : <T>(in1: Iterable<T>, in2: ArrayLike<T>) => ArrayLike<T> | Iterable<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>) => Iterable<T> | ArrayLike<T>
166+
>getEither : <T>(in1: Iterable<T>, in2: ArrayLike<T>) => ArrayLike<T> | Iterable<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 : Iterable<T> | ArrayLike<T>
171+
>Math.random() > 0.5 ? in1 : in2 : ArrayLike<T> | Iterable<T>
172172
>Math.random() > 0.5 : boolean
173173
>Math.random() : number
174174
>Math.random : () => number
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
circularReferenceInReturnType.ts(3,7): error TS7022: 'res1' implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer.
2+
circularReferenceInReturnType.ts(9,7): error TS7022: 'res3' implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer.
3+
4+
5+
==== circularReferenceInReturnType.ts (2 errors) ====
6+
// inference fails for res1 and res2, but ideally should not
7+
declare function fn1<T>(cb: () => T): string;
8+
const res1 = fn1(() => res1);
9+
~~~~
10+
!!! error TS7022: 'res1' implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer.
11+
12+
declare function fn2<T>(): (cb: () => any) => (a: T) => void;
13+
const res2 = fn2()(() => res2);
14+
15+
declare function fn3<T>(): <T2>(cb: (arg: T2) => any) => (a: T) => void;
16+
const res3 = fn3()(() => res3);
17+
~~~~
18+
!!! error TS7022: 'res3' implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer.
19+
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,42 @@
11
//// [tests/cases/compiler/circularReferenceInReturnType.ts] ////
22

33
=== circularReferenceInReturnType.ts ===
4+
// inference fails for res1 and res2, but ideally should not
45
declare function fn1<T>(cb: () => T): string;
56
>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))
7+
>T : Symbol(T, Decl(circularReferenceInReturnType.ts, 1, 21))
8+
>cb : Symbol(cb, Decl(circularReferenceInReturnType.ts, 1, 24))
9+
>T : Symbol(T, Decl(circularReferenceInReturnType.ts, 1, 21))
910

1011
const res1 = fn1(() => res1);
11-
>res1 : Symbol(res1, Decl(circularReferenceInReturnType.ts, 1, 5))
12+
>res1 : Symbol(res1, Decl(circularReferenceInReturnType.ts, 2, 5))
1213
>fn1 : Symbol(fn1, Decl(circularReferenceInReturnType.ts, 0, 0))
13-
>res1 : Symbol(res1, Decl(circularReferenceInReturnType.ts, 1, 5))
14+
>res1 : Symbol(res1, Decl(circularReferenceInReturnType.ts, 2, 5))
1415

1516
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))
17+
>fn2 : Symbol(fn2, Decl(circularReferenceInReturnType.ts, 2, 29))
18+
>T : Symbol(T, Decl(circularReferenceInReturnType.ts, 4, 21))
19+
>cb : Symbol(cb, Decl(circularReferenceInReturnType.ts, 4, 28))
20+
>a : Symbol(a, Decl(circularReferenceInReturnType.ts, 4, 47))
21+
>T : Symbol(T, Decl(circularReferenceInReturnType.ts, 4, 21))
2122

2223
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))
24+
>res2 : Symbol(res2, Decl(circularReferenceInReturnType.ts, 5, 5))
25+
>fn2 : Symbol(fn2, Decl(circularReferenceInReturnType.ts, 2, 29))
26+
>res2 : Symbol(res2, Decl(circularReferenceInReturnType.ts, 5, 5))
2627

2728
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))
29+
>fn3 : Symbol(fn3, Decl(circularReferenceInReturnType.ts, 5, 31))
30+
>T : Symbol(T, Decl(circularReferenceInReturnType.ts, 7, 21))
31+
>T2 : Symbol(T2, Decl(circularReferenceInReturnType.ts, 7, 28))
32+
>cb : Symbol(cb, Decl(circularReferenceInReturnType.ts, 7, 32))
33+
>arg : Symbol(arg, Decl(circularReferenceInReturnType.ts, 7, 37))
34+
>T2 : Symbol(T2, Decl(circularReferenceInReturnType.ts, 7, 28))
35+
>a : Symbol(a, Decl(circularReferenceInReturnType.ts, 7, 58))
36+
>T : Symbol(T, Decl(circularReferenceInReturnType.ts, 7, 21))
3637

3738
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))
39+
>res3 : Symbol(res3, Decl(circularReferenceInReturnType.ts, 8, 5))
40+
>fn3 : Symbol(fn3, Decl(circularReferenceInReturnType.ts, 5, 31))
41+
>res3 : Symbol(res3, Decl(circularReferenceInReturnType.ts, 8, 5))
4142

tests/baselines/reference/circularReferenceInReturnType.types

+7-6
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,17 @@
11
//// [tests/cases/compiler/circularReferenceInReturnType.ts] ////
22

33
=== circularReferenceInReturnType.ts ===
4+
// inference fails for res1 and res2, but ideally should not
45
declare function fn1<T>(cb: () => T): string;
56
>fn1 : <T>(cb: () => T) => string
67
>cb : () => T
78

89
const res1 = fn1(() => res1);
9-
>res1 : string
10+
>res1 : any
1011
>fn1(() => res1) : string
1112
>fn1 : <T>(cb: () => T) => string
12-
>() => res1 : () => string
13-
>res1 : string
13+
>() => res1 : () => any
14+
>res1 : any
1415

1516
declare function fn2<T>(): (cb: () => any) => (a: T) => void;
1617
>fn2 : <T>() => (cb: () => any) => (a: T) => void
@@ -32,10 +33,10 @@ declare function fn3<T>(): <T2>(cb: (arg: T2) => any) => (a: T) => void;
3233
>a : T
3334

3435
const res3 = fn3()(() => res3);
35-
>res3 : (a: unknown) => void
36+
>res3 : any
3637
>fn3()(() => res3) : (a: unknown) => void
3738
>fn3() : <T2>(cb: (arg: T2) => any) => (a: unknown) => void
3839
>fn3 : <T>() => <T2>(cb: (arg: T2) => any) => (a: T) => void
39-
>() => res3 : () => (a: unknown) => void
40-
>res3 : (a: unknown) => void
40+
>() => res3 : () => any
41+
>res3 : any
4142

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
circularReferenceInReturnType2.ts(39,7): error TS7022: 'A' implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer.
2+
3+
4+
==== circularReferenceInReturnType2.ts (1 errors) ====
5+
type ObjectType<Source> = {
6+
kind: "object";
7+
__source: (source: Source) => void;
8+
};
9+
10+
type Field<Source, Key extends string> = {
11+
__key: (key: Key) => void;
12+
__source: (source: Source) => void;
13+
};
14+
15+
declare const object: <Source>() => <
16+
Fields extends {
17+
[Key in keyof Fields]: Field<Source, Key & string>;
18+
}
19+
>(config: {
20+
name: string;
21+
fields: Fields | (() => Fields);
22+
}) => ObjectType<Source>;
23+
24+
type InferValueFromObjectType<Type extends ObjectType<any>> =
25+
Type extends ObjectType<infer Source> ? Source : never;
26+
27+
type FieldResolver<Source, TType extends ObjectType<any>> = (
28+
source: Source
29+
) => InferValueFromObjectType<TType>;
30+
31+
type FieldFuncArgs<Source, Type extends ObjectType<any>> = {
32+
type: Type;
33+
resolve: FieldResolver<Source, Type>;
34+
};
35+
36+
declare const field: <Source, Type extends ObjectType<any>, Key extends string>(
37+
field: FieldFuncArgs<Source, Type>
38+
) => Field<Source, Key>;
39+
40+
type Something = { foo: number };
41+
42+
// inference fails here, but ideally should not
43+
const A = object<Something>()({
44+
~
45+
!!! error TS7022: 'A' implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer.
46+
name: "A",
47+
fields: () => ({
48+
a: field({
49+
type: A,
50+
resolve() {
51+
return {
52+
foo: 100,
53+
};
54+
},
55+
}),
56+
}),
57+
});
58+

tests/baselines/reference/circularReferenceInReturnType2.symbols

+9-8
Original file line numberDiff line numberDiff line change
@@ -126,31 +126,32 @@ type Something = { foo: number };
126126
>Something : Symbol(Something, Decl(circularReferenceInReturnType2.ts, 33, 24))
127127
>foo : Symbol(foo, Decl(circularReferenceInReturnType2.ts, 35, 18))
128128

129+
// inference fails here, but ideally should not
129130
const A = object<Something>()({
130-
>A : Symbol(A, Decl(circularReferenceInReturnType2.ts, 37, 5))
131+
>A : Symbol(A, Decl(circularReferenceInReturnType2.ts, 38, 5))
131132
>object : Symbol(object, Decl(circularReferenceInReturnType2.ts, 10, 13))
132133
>Something : Symbol(Something, Decl(circularReferenceInReturnType2.ts, 33, 24))
133134

134135
name: "A",
135-
>name : Symbol(name, Decl(circularReferenceInReturnType2.ts, 37, 31))
136+
>name : Symbol(name, Decl(circularReferenceInReturnType2.ts, 38, 31))
136137

137138
fields: () => ({
138-
>fields : Symbol(fields, Decl(circularReferenceInReturnType2.ts, 38, 12))
139+
>fields : Symbol(fields, Decl(circularReferenceInReturnType2.ts, 39, 12))
139140

140141
a: field({
141-
>a : Symbol(a, Decl(circularReferenceInReturnType2.ts, 39, 18))
142+
>a : Symbol(a, Decl(circularReferenceInReturnType2.ts, 40, 18))
142143
>field : Symbol(field, Decl(circularReferenceInReturnType2.ts, 31, 13))
143144

144145
type: A,
145-
>type : Symbol(type, Decl(circularReferenceInReturnType2.ts, 40, 14))
146-
>A : Symbol(A, Decl(circularReferenceInReturnType2.ts, 37, 5))
146+
>type : Symbol(type, Decl(circularReferenceInReturnType2.ts, 41, 14))
147+
>A : Symbol(A, Decl(circularReferenceInReturnType2.ts, 38, 5))
147148

148149
resolve() {
149-
>resolve : Symbol(resolve, Decl(circularReferenceInReturnType2.ts, 41, 14))
150+
>resolve : Symbol(resolve, Decl(circularReferenceInReturnType2.ts, 42, 14))
150151

151152
return {
152153
foo: 100,
153-
>foo : Symbol(foo, Decl(circularReferenceInReturnType2.ts, 43, 16))
154+
>foo : Symbol(foo, Decl(circularReferenceInReturnType2.ts, 44, 16))
154155

155156
};
156157
},

tests/baselines/reference/circularReferenceInReturnType2.types

+5-4
Original file line numberDiff line numberDiff line change
@@ -79,8 +79,9 @@ type Something = { foo: number };
7979
>Something : { foo: number; }
8080
>foo : number
8181

82+
// inference fails here, but ideally should not
8283
const A = object<Something>()({
83-
>A : ObjectType<Something>
84+
>A : any
8485
>object<Something>()({ name: "A", fields: () => ({ a: field({ type: A, resolve() { return { foo: 100, }; }, }), }),}) : ObjectType<Something>
8586
>object<Something>() : <Fields extends { [Key in keyof Fields]: Field<Something, Key & string>; }>(config: { name: string; fields: Fields | (() => Fields); }) => ObjectType<Something>
8687
>object : <Source>() => <Fields extends { [Key in keyof Fields]: Field<Source, Key & string>; }>(config: { name: string; fields: Fields | (() => Fields); }) => ObjectType<Source>
@@ -100,11 +101,11 @@ const A = object<Something>()({
100101
>a : Field<Something, "a">
101102
>field({ type: A, resolve() { return { foo: 100, }; }, }) : Field<Something, "a">
102103
>field : <Source, Type extends ObjectType<any>, Key extends string>(field: FieldFuncArgs<Source, Type>) => Field<Source, Key>
103-
>{ type: A, resolve() { return { foo: 100, }; }, } : { type: ObjectType<Something>; resolve(): { foo: number; }; }
104+
>{ type: A, resolve() { return { foo: 100, }; }, } : { type: any; resolve(): { foo: number; }; }
104105

105106
type: A,
106-
>type : ObjectType<Something>
107-
>A : ObjectType<Something>
107+
>type : any
108+
>A : any
108109

109110
resolve() {
110111
>resolve : () => { foo: number; }

tests/cases/compiler/circularReferenceInReturnType.ts

+1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// @strict: true
22
// @noEmit: true
33

4+
// inference fails for res1 and res2, but ideally should not
45
declare function fn1<T>(cb: () => T): string;
56
const res1 = fn1(() => res1);
67

tests/cases/compiler/circularReferenceInReturnType2.ts

+1
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ declare const field: <Source, Type extends ObjectType<any>, Key extends string>(
3838

3939
type Something = { foo: number };
4040

41+
// inference fails here, but ideally should not
4142
const A = object<Something>()({
4243
name: "A",
4344
fields: () => ({

0 commit comments

Comments
 (0)