Skip to content

Commit c3ae7c4

Browse files
authored
Revert return type narrowing (#61136)
1 parent 71716a2 commit c3ae7c4

38 files changed

+311
-12191
lines changed

src/compiler/checker.ts

+19-358
Large diffs are not rendered by default.

src/compiler/types.ts

+2-9
Original file line numberDiff line numberDiff line change
@@ -6249,7 +6249,6 @@ export interface NodeLinks {
62496249
decoratorSignature?: Signature; // Signature for decorator as if invoked by the runtime.
62506250
spreadIndices?: { first: number | undefined, last: number | undefined }; // Indices of first and last spread elements in array literal
62516251
parameterInitializerContainsUndefined?: boolean; // True if this is a parameter declaration whose type annotation contains "undefined".
6252-
contextualReturnType?: Type; // If the node is a return statement's expression, then this is the contextual return type.
62536252
fakeScopeForSignatureDeclaration?: "params" | "typeParams"; // If present, this is a fake scope injected into an enclosing declaration chain.
62546253
assertionExpressionType?: Type; // Cached type of the expression of a type assertion
62556254
potentialThisCollisions?: Node[];
@@ -6517,8 +6516,6 @@ export const enum ObjectFlags {
65176516
IsGenericIndexType = 1 << 23, // Union or intersection contains generic index type
65186517
/** @internal */
65196518
IsGenericType = IsGenericObjectType | IsGenericIndexType,
6520-
/** @internal */
6521-
IsNarrowingType = 1 << 24, // Substitution type that comes from type narrowing
65226519

65236520
// Flags that require TypeFlags.Union
65246521
/** @internal */
@@ -6918,16 +6915,12 @@ export interface StringMappingType extends InstantiableType {
69186915
}
69196916

69206917
// Type parameter substitution (TypeFlags.Substitution)
6921-
// - Substitution types are created for type parameters or indexed access types that occur in the
6918+
// Substitution types are created for type parameters or indexed access types that occur in the
69226919
// true branch of a conditional type. For example, in 'T extends string ? Foo<T> : Bar<T>', the
69236920
// reference to T in Foo<T> is resolved as a substitution type that substitutes 'string & T' for T.
69246921
// Thus, if Foo has a 'string' constraint on its type parameter, T will satisfy it.
6925-
// - Substitution types are also created for NoInfer<T> types. Those are represented as substitution
6922+
// Substitution type are also created for NoInfer<T> types. Those are represented as substitution
69266923
// types where the constraint is type 'unknown' (which is never generated for the case above).
6927-
// - Substitution types are also created for return type narrowing:
6928-
// if a type parameter `T` is linked to a parameter `x` and `x`'s narrowed type is `S`,
6929-
// we represent that with a substitution type with base `T` and constraint `S`.
6930-
// The resulting substitution type has `ObjectFlags.IsNarrowedType` set.
69316924
export interface SubstitutionType extends InstantiableType {
69326925
objectFlags: ObjectFlags;
69336926
baseType: Type; // Target type
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
conditionalReturnExpression.ts(2,18): error TS2322: Type '1' is not assignable to type '3'.
2+
conditionalReturnExpression.ts(2,23): error TS2322: Type '2' is not assignable to type '3'.
3+
conditionalReturnExpression.ts(8,43): error TS2322: Type 'number' is not assignable to type 'string'.
4+
conditionalReturnExpression.ts(19,71): error TS2322: Type 'number' is not assignable to type 'string'.
5+
6+
7+
==== conditionalReturnExpression.ts (4 errors) ====
8+
function return1(x: boolean): 3 {
9+
return (x ? (1) : 2);
10+
~
11+
!!! error TS2322: Type '1' is not assignable to type '3'.
12+
~
13+
!!! error TS2322: Type '2' is not assignable to type '3'.
14+
}
15+
16+
declare function getAny(): any;
17+
18+
function return2(x: string): string {
19+
return x.startsWith("a") ? getAny() : 1;
20+
~
21+
!!! error TS2322: Type 'number' is not assignable to type 'string'.
22+
}
23+
24+
function return3(x: string): string {
25+
return x.startsWith("a") ? "a" : x;
26+
}
27+
28+
function return4(x: string): string {
29+
return (x.startsWith("a") ? getAny() : 1) as string;
30+
}
31+
32+
const return5 = (x: string): string => x.startsWith("a") ? getAny() : 1;
33+
~
34+
!!! error TS2322: Type 'number' is not assignable to type 'string'.
35+
36+
const return6 = (x: string): string => (x.startsWith("a") ? getAny() : 1) as string;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
//// [tests/cases/compiler/conditionalReturnExpression.ts] ////
2+
3+
=== conditionalReturnExpression.ts ===
4+
function return1(x: boolean): 3 {
5+
>return1 : Symbol(return1, Decl(conditionalReturnExpression.ts, 0, 0))
6+
>x : Symbol(x, Decl(conditionalReturnExpression.ts, 0, 17))
7+
8+
return (x ? (1) : 2);
9+
>x : Symbol(x, Decl(conditionalReturnExpression.ts, 0, 17))
10+
}
11+
12+
declare function getAny(): any;
13+
>getAny : Symbol(getAny, Decl(conditionalReturnExpression.ts, 2, 1))
14+
15+
function return2(x: string): string {
16+
>return2 : Symbol(return2, Decl(conditionalReturnExpression.ts, 4, 31))
17+
>x : Symbol(x, Decl(conditionalReturnExpression.ts, 6, 17))
18+
19+
return x.startsWith("a") ? getAny() : 1;
20+
>x.startsWith : Symbol(String.startsWith, Decl(lib.es2015.core.d.ts, --, --))
21+
>x : Symbol(x, Decl(conditionalReturnExpression.ts, 6, 17))
22+
>startsWith : Symbol(String.startsWith, Decl(lib.es2015.core.d.ts, --, --))
23+
>getAny : Symbol(getAny, Decl(conditionalReturnExpression.ts, 2, 1))
24+
}
25+
26+
function return3(x: string): string {
27+
>return3 : Symbol(return3, Decl(conditionalReturnExpression.ts, 8, 1))
28+
>x : Symbol(x, Decl(conditionalReturnExpression.ts, 10, 17))
29+
30+
return x.startsWith("a") ? "a" : x;
31+
>x.startsWith : Symbol(String.startsWith, Decl(lib.es2015.core.d.ts, --, --))
32+
>x : Symbol(x, Decl(conditionalReturnExpression.ts, 10, 17))
33+
>startsWith : Symbol(String.startsWith, Decl(lib.es2015.core.d.ts, --, --))
34+
>x : Symbol(x, Decl(conditionalReturnExpression.ts, 10, 17))
35+
}
36+
37+
function return4(x: string): string {
38+
>return4 : Symbol(return4, Decl(conditionalReturnExpression.ts, 12, 1))
39+
>x : Symbol(x, Decl(conditionalReturnExpression.ts, 14, 17))
40+
41+
return (x.startsWith("a") ? getAny() : 1) as string;
42+
>x.startsWith : Symbol(String.startsWith, Decl(lib.es2015.core.d.ts, --, --))
43+
>x : Symbol(x, Decl(conditionalReturnExpression.ts, 14, 17))
44+
>startsWith : Symbol(String.startsWith, Decl(lib.es2015.core.d.ts, --, --))
45+
>getAny : Symbol(getAny, Decl(conditionalReturnExpression.ts, 2, 1))
46+
}
47+
48+
const return5 = (x: string): string => x.startsWith("a") ? getAny() : 1;
49+
>return5 : Symbol(return5, Decl(conditionalReturnExpression.ts, 18, 5))
50+
>x : Symbol(x, Decl(conditionalReturnExpression.ts, 18, 17))
51+
>x.startsWith : Symbol(String.startsWith, Decl(lib.es2015.core.d.ts, --, --))
52+
>x : Symbol(x, Decl(conditionalReturnExpression.ts, 18, 17))
53+
>startsWith : Symbol(String.startsWith, Decl(lib.es2015.core.d.ts, --, --))
54+
>getAny : Symbol(getAny, Decl(conditionalReturnExpression.ts, 2, 1))
55+
56+
const return6 = (x: string): string => (x.startsWith("a") ? getAny() : 1) as string;
57+
>return6 : Symbol(return6, Decl(conditionalReturnExpression.ts, 20, 5))
58+
>x : Symbol(x, Decl(conditionalReturnExpression.ts, 20, 17))
59+
>x.startsWith : Symbol(String.startsWith, Decl(lib.es2015.core.d.ts, --, --))
60+
>x : Symbol(x, Decl(conditionalReturnExpression.ts, 20, 17))
61+
>startsWith : Symbol(String.startsWith, Decl(lib.es2015.core.d.ts, --, --))
62+
>getAny : Symbol(getAny, Decl(conditionalReturnExpression.ts, 2, 1))
63+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
//// [tests/cases/compiler/conditionalReturnExpression.ts] ////
2+
3+
=== conditionalReturnExpression.ts ===
4+
function return1(x: boolean): 3 {
5+
>return1 : (x: boolean) => 3
6+
> : ^ ^^ ^^^^^
7+
>x : boolean
8+
> : ^^^^^^^
9+
10+
return (x ? (1) : 2);
11+
>(x ? (1) : 2) : 1 | 2
12+
> : ^^^^^
13+
>x ? (1) : 2 : 1 | 2
14+
> : ^^^^^
15+
>x : boolean
16+
> : ^^^^^^^
17+
>(1) : 1
18+
> : ^
19+
>1 : 1
20+
> : ^
21+
>2 : 2
22+
> : ^
23+
}
24+
25+
declare function getAny(): any;
26+
>getAny : () => any
27+
> : ^^^^^^
28+
29+
function return2(x: string): string {
30+
>return2 : (x: string) => string
31+
> : ^ ^^ ^^^^^
32+
>x : string
33+
> : ^^^^^^
34+
35+
return x.startsWith("a") ? getAny() : 1;
36+
>x.startsWith("a") ? getAny() : 1 : any
37+
> : ^^^
38+
>x.startsWith("a") : boolean
39+
> : ^^^^^^^
40+
>x.startsWith : (searchString: string, position?: number) => boolean
41+
> : ^ ^^ ^^ ^^^ ^^^^^
42+
>x : string
43+
> : ^^^^^^
44+
>startsWith : (searchString: string, position?: number) => boolean
45+
> : ^ ^^ ^^ ^^^ ^^^^^
46+
>"a" : "a"
47+
> : ^^^
48+
>getAny() : any
49+
> : ^^^
50+
>getAny : () => any
51+
> : ^^^^^^
52+
>1 : 1
53+
> : ^
54+
}
55+
56+
function return3(x: string): string {
57+
>return3 : (x: string) => string
58+
> : ^ ^^ ^^^^^
59+
>x : string
60+
> : ^^^^^^
61+
62+
return x.startsWith("a") ? "a" : x;
63+
>x.startsWith("a") ? "a" : x : string
64+
> : ^^^^^^
65+
>x.startsWith("a") : boolean
66+
> : ^^^^^^^
67+
>x.startsWith : (searchString: string, position?: number) => boolean
68+
> : ^ ^^ ^^ ^^^ ^^^^^
69+
>x : string
70+
> : ^^^^^^
71+
>startsWith : (searchString: string, position?: number) => boolean
72+
> : ^ ^^ ^^ ^^^ ^^^^^
73+
>"a" : "a"
74+
> : ^^^
75+
>"a" : "a"
76+
> : ^^^
77+
>x : string
78+
> : ^^^^^^
79+
}
80+
81+
function return4(x: string): string {
82+
>return4 : (x: string) => string
83+
> : ^ ^^ ^^^^^
84+
>x : string
85+
> : ^^^^^^
86+
87+
return (x.startsWith("a") ? getAny() : 1) as string;
88+
>(x.startsWith("a") ? getAny() : 1) as string : string
89+
> : ^^^^^^
90+
>(x.startsWith("a") ? getAny() : 1) : any
91+
> : ^^^
92+
>x.startsWith("a") ? getAny() : 1 : any
93+
> : ^^^
94+
>x.startsWith("a") : boolean
95+
> : ^^^^^^^
96+
>x.startsWith : (searchString: string, position?: number) => boolean
97+
> : ^ ^^ ^^ ^^^ ^^^^^
98+
>x : string
99+
> : ^^^^^^
100+
>startsWith : (searchString: string, position?: number) => boolean
101+
> : ^ ^^ ^^ ^^^ ^^^^^
102+
>"a" : "a"
103+
> : ^^^
104+
>getAny() : any
105+
> : ^^^
106+
>getAny : () => any
107+
> : ^^^^^^
108+
>1 : 1
109+
> : ^
110+
}
111+
112+
const return5 = (x: string): string => x.startsWith("a") ? getAny() : 1;
113+
>return5 : (x: string) => string
114+
> : ^ ^^ ^^^^^
115+
>(x: string): string => x.startsWith("a") ? getAny() : 1 : (x: string) => string
116+
> : ^ ^^ ^^^^^
117+
>x : string
118+
> : ^^^^^^
119+
>x.startsWith("a") ? getAny() : 1 : any
120+
> : ^^^
121+
>x.startsWith("a") : boolean
122+
> : ^^^^^^^
123+
>x.startsWith : (searchString: string, position?: number) => boolean
124+
> : ^ ^^ ^^ ^^^ ^^^^^
125+
>x : string
126+
> : ^^^^^^
127+
>startsWith : (searchString: string, position?: number) => boolean
128+
> : ^ ^^ ^^ ^^^ ^^^^^
129+
>"a" : "a"
130+
> : ^^^
131+
>getAny() : any
132+
> : ^^^
133+
>getAny : () => any
134+
> : ^^^^^^
135+
>1 : 1
136+
> : ^
137+
138+
const return6 = (x: string): string => (x.startsWith("a") ? getAny() : 1) as string;
139+
>return6 : (x: string) => string
140+
> : ^ ^^ ^^^^^
141+
>(x: string): string => (x.startsWith("a") ? getAny() : 1) as string : (x: string) => string
142+
> : ^ ^^ ^^^^^
143+
>x : string
144+
> : ^^^^^^
145+
>(x.startsWith("a") ? getAny() : 1) as string : string
146+
> : ^^^^^^
147+
>(x.startsWith("a") ? getAny() : 1) : any
148+
> : ^^^
149+
>x.startsWith("a") ? getAny() : 1 : any
150+
> : ^^^
151+
>x.startsWith("a") : boolean
152+
> : ^^^^^^^
153+
>x.startsWith : (searchString: string, position?: number) => boolean
154+
> : ^ ^^ ^^ ^^^ ^^^^^
155+
>x : string
156+
> : ^^^^^^
157+
>startsWith : (searchString: string, position?: number) => boolean
158+
> : ^ ^^ ^^ ^^^ ^^^^^
159+
>"a" : "a"
160+
> : ^^^
161+
>getAny() : any
162+
> : ^^^
163+
>getAny : () => any
164+
> : ^^^^^^
165+
>1 : 1
166+
> : ^
167+

0 commit comments

Comments
 (0)