Skip to content

Commit 569cdf1

Browse files
authored
Disallow expression with type parameters as left side of property access (#49464)
1 parent ad6d086 commit 569cdf1

22 files changed

+433
-5
lines changed

src/compiler/diagnosticMessages.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1493,6 +1493,10 @@
14931493
"category": "Message",
14941494
"code": 1476
14951495
},
1496+
"An instantiation expression cannot be followed by a property access.": {
1497+
"category": "Error",
1498+
"code": 1477
1499+
},
14961500

14971501
"The types of '{0}' are incompatible between these types.": {
14981502
"category": "Error",

src/compiler/parser.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5646,6 +5646,11 @@ namespace ts {
56465646
if (isOptionalChain && isPrivateIdentifier(propertyAccess.name)) {
56475647
parseErrorAtRange(propertyAccess.name, Diagnostics.An_optional_chain_cannot_contain_private_identifiers);
56485648
}
5649+
if (isExpressionWithTypeArguments(expression) && expression.typeArguments) {
5650+
const pos = expression.typeArguments.pos - 1;
5651+
const end = skipTrivia(sourceText, expression.typeArguments.end) + 1;
5652+
parseErrorAt(pos, end, Diagnostics.An_instantiation_expression_cannot_be_followed_by_a_property_access);
5653+
}
56495654
return finishNode(propertyAccess, pos);
56505655
}
56515656

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
1+
tests/cases/compiler/genericCallWithoutArgs.ts(4,2): error TS1477: An instantiation expression cannot be followed by a property access.
12
tests/cases/compiler/genericCallWithoutArgs.ts(4,18): error TS1003: Identifier expected.
23

34

4-
==== tests/cases/compiler/genericCallWithoutArgs.ts (1 errors) ====
5+
==== tests/cases/compiler/genericCallWithoutArgs.ts (2 errors) ====
56
function f<X, Y>(x: X, y: Y) {
67
}
78

89
f<number,string>.
10+
~~~~~~~~~~~~~~~
11+
!!! error TS1477: An instantiation expression cannot be followed by a property access.
912

1013
!!! error TS1003: Identifier expected.

tests/baselines/reference/instantiationExpressionErrors.errors.txt

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
tests/cases/conformance/types/typeParameters/typeArgumentLists/instantiationExpressionErrors.ts(7,13): error TS1477: An instantiation expression cannot be followed by a property access.
2+
tests/cases/conformance/types/typeParameters/typeArgumentLists/instantiationExpressionErrors.ts(8,13): error TS1477: An instantiation expression cannot be followed by a property access.
13
tests/cases/conformance/types/typeParameters/typeArgumentLists/instantiationExpressionErrors.ts(13,12): error TS2365: Operator '>' cannot be applied to types 'boolean' and 'string[]'.
24
tests/cases/conformance/types/typeParameters/typeArgumentLists/instantiationExpressionErrors.ts(13,14): error TS2693: 'number' only refers to a type, but is being used as a value here.
35
tests/cases/conformance/types/typeParameters/typeArgumentLists/instantiationExpressionErrors.ts(18,12): error TS2365: Operator '>' cannot be applied to types 'boolean' and 'number'.
@@ -14,15 +16,19 @@ tests/cases/conformance/types/typeParameters/typeArgumentLists/instantiationExpr
1416
tests/cases/conformance/types/typeParameters/typeArgumentLists/instantiationExpressionErrors.ts(45,12): error TS2365: Operator '>' cannot be applied to types 'boolean' and 'number'.
1517

1618

17-
==== tests/cases/conformance/types/typeParameters/typeArgumentLists/instantiationExpressionErrors.ts (14 errors) ====
19+
==== tests/cases/conformance/types/typeParameters/typeArgumentLists/instantiationExpressionErrors.ts (16 errors) ====
1820
declare let f: { <T>(): T, g<U>(): U };
1921

2022
// Type arguments in member expressions
2123

2224
const a1 = f<number>; // { (): number; g<U>(): U; }
2325
const a2 = f.g<number>; // () => number
2426
const a3 = f<number>.g; // <U>() => U
27+
~~~~~~~~
28+
!!! error TS1477: An instantiation expression cannot be followed by a property access.
2529
const a4 = f<number>.g<number>; // () => number
30+
~~~~~~~~
31+
!!! error TS1477: An instantiation expression cannot be followed by a property access.
2632
const a5 = f['g']<number>; // () => number
2733

2834
// `[` is an expression starter and cannot immediately follow a type argument list
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
tests/cases/compiler/optionalChainWithInstantiationExpression1.ts(12,5): error TS1477: An instantiation expression cannot be followed by a property access.
2+
3+
4+
==== tests/cases/compiler/optionalChainWithInstantiationExpression1.ts (1 errors) ====
5+
declare namespace A {
6+
export class b<T> {
7+
static d: number;
8+
constructor(x: T);
9+
}
10+
}
11+
12+
type c = unknown;
13+
14+
declare const a: typeof A | undefined;
15+
16+
a?.b<c>.d;
17+
~~~
18+
!!! error TS1477: An instantiation expression cannot be followed by a property access.
19+
20+
a?.b.d
21+
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
//// [optionalChainWithInstantiationExpression1.ts]
2+
declare namespace A {
3+
export class b<T> {
4+
static d: number;
5+
constructor(x: T);
6+
}
7+
}
8+
9+
type c = unknown;
10+
11+
declare const a: typeof A | undefined;
12+
13+
a?.b<c>.d;
14+
15+
a?.b.d
16+
17+
18+
//// [optionalChainWithInstantiationExpression1.js]
19+
(a === null || a === void 0 ? void 0 : a.b).d;
20+
a === null || a === void 0 ? void 0 : a.b.d;
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
=== tests/cases/compiler/optionalChainWithInstantiationExpression1.ts ===
2+
declare namespace A {
3+
>A : Symbol(A, Decl(optionalChainWithInstantiationExpression1.ts, 0, 0))
4+
5+
export class b<T> {
6+
>b : Symbol(b, Decl(optionalChainWithInstantiationExpression1.ts, 0, 21))
7+
>T : Symbol(T, Decl(optionalChainWithInstantiationExpression1.ts, 1, 19))
8+
9+
static d: number;
10+
>d : Symbol(b.d, Decl(optionalChainWithInstantiationExpression1.ts, 1, 23))
11+
12+
constructor(x: T);
13+
>x : Symbol(x, Decl(optionalChainWithInstantiationExpression1.ts, 3, 20))
14+
>T : Symbol(T, Decl(optionalChainWithInstantiationExpression1.ts, 1, 19))
15+
}
16+
}
17+
18+
type c = unknown;
19+
>c : Symbol(c, Decl(optionalChainWithInstantiationExpression1.ts, 5, 1))
20+
21+
declare const a: typeof A | undefined;
22+
>a : Symbol(a, Decl(optionalChainWithInstantiationExpression1.ts, 9, 13))
23+
>A : Symbol(A, Decl(optionalChainWithInstantiationExpression1.ts, 0, 0))
24+
25+
a?.b<c>.d;
26+
>a?.b<c>.d : Symbol(A.b.d, Decl(optionalChainWithInstantiationExpression1.ts, 1, 23))
27+
>a?.b : Symbol(A.b, Decl(optionalChainWithInstantiationExpression1.ts, 0, 21))
28+
>a : Symbol(a, Decl(optionalChainWithInstantiationExpression1.ts, 9, 13))
29+
>b : Symbol(A.b, Decl(optionalChainWithInstantiationExpression1.ts, 0, 21))
30+
>c : Symbol(c, Decl(optionalChainWithInstantiationExpression1.ts, 5, 1))
31+
>d : Symbol(A.b.d, Decl(optionalChainWithInstantiationExpression1.ts, 1, 23))
32+
33+
a?.b.d
34+
>a?.b.d : Symbol(A.b.d, Decl(optionalChainWithInstantiationExpression1.ts, 1, 23))
35+
>a?.b : Symbol(A.b, Decl(optionalChainWithInstantiationExpression1.ts, 0, 21))
36+
>a : Symbol(a, Decl(optionalChainWithInstantiationExpression1.ts, 9, 13))
37+
>b : Symbol(A.b, Decl(optionalChainWithInstantiationExpression1.ts, 0, 21))
38+
>d : Symbol(A.b.d, Decl(optionalChainWithInstantiationExpression1.ts, 1, 23))
39+
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
=== tests/cases/compiler/optionalChainWithInstantiationExpression1.ts ===
2+
declare namespace A {
3+
>A : typeof A
4+
5+
export class b<T> {
6+
>b : b<T>
7+
8+
static d: number;
9+
>d : number
10+
11+
constructor(x: T);
12+
>x : T
13+
}
14+
}
15+
16+
type c = unknown;
17+
>c : unknown
18+
19+
declare const a: typeof A | undefined;
20+
>a : typeof A
21+
>A : typeof A
22+
23+
a?.b<c>.d;
24+
>a?.b<c>.d : number
25+
>a?.b<c> : { new (x: unknown): A.b<unknown>; prototype: A.b<any>; d: number; }
26+
>a?.b : typeof A.b
27+
>a : typeof A
28+
>b : typeof A.b
29+
>d : number
30+
31+
a?.b.d
32+
>a?.b.d : number
33+
>a?.b : typeof A.b
34+
>a : typeof A
35+
>b : typeof A.b
36+
>d : number
37+
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
tests/cases/compiler/optionalChainWithInstantiationExpression1.ts(12,5): error TS1477: An instantiation expression cannot be followed by a property access.
2+
3+
4+
==== tests/cases/compiler/optionalChainWithInstantiationExpression1.ts (1 errors) ====
5+
declare namespace A {
6+
export class b<T> {
7+
static d: number;
8+
constructor(x: T);
9+
}
10+
}
11+
12+
type c = unknown;
13+
14+
declare const a: typeof A | undefined;
15+
16+
a?.b<c>.d;
17+
~~~
18+
!!! error TS1477: An instantiation expression cannot be followed by a property access.
19+
20+
a?.b.d
21+
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
//// [optionalChainWithInstantiationExpression1.ts]
2+
declare namespace A {
3+
export class b<T> {
4+
static d: number;
5+
constructor(x: T);
6+
}
7+
}
8+
9+
type c = unknown;
10+
11+
declare const a: typeof A | undefined;
12+
13+
a?.b<c>.d;
14+
15+
a?.b.d
16+
17+
18+
//// [optionalChainWithInstantiationExpression1.js]
19+
a?.b.d;
20+
a?.b.d;
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
=== tests/cases/compiler/optionalChainWithInstantiationExpression1.ts ===
2+
declare namespace A {
3+
>A : Symbol(A, Decl(optionalChainWithInstantiationExpression1.ts, 0, 0))
4+
5+
export class b<T> {
6+
>b : Symbol(b, Decl(optionalChainWithInstantiationExpression1.ts, 0, 21))
7+
>T : Symbol(T, Decl(optionalChainWithInstantiationExpression1.ts, 1, 19))
8+
9+
static d: number;
10+
>d : Symbol(b.d, Decl(optionalChainWithInstantiationExpression1.ts, 1, 23))
11+
12+
constructor(x: T);
13+
>x : Symbol(x, Decl(optionalChainWithInstantiationExpression1.ts, 3, 20))
14+
>T : Symbol(T, Decl(optionalChainWithInstantiationExpression1.ts, 1, 19))
15+
}
16+
}
17+
18+
type c = unknown;
19+
>c : Symbol(c, Decl(optionalChainWithInstantiationExpression1.ts, 5, 1))
20+
21+
declare const a: typeof A | undefined;
22+
>a : Symbol(a, Decl(optionalChainWithInstantiationExpression1.ts, 9, 13))
23+
>A : Symbol(A, Decl(optionalChainWithInstantiationExpression1.ts, 0, 0))
24+
25+
a?.b<c>.d;
26+
>a?.b<c>.d : Symbol(A.b.d, Decl(optionalChainWithInstantiationExpression1.ts, 1, 23))
27+
>a?.b : Symbol(A.b, Decl(optionalChainWithInstantiationExpression1.ts, 0, 21))
28+
>a : Symbol(a, Decl(optionalChainWithInstantiationExpression1.ts, 9, 13))
29+
>b : Symbol(A.b, Decl(optionalChainWithInstantiationExpression1.ts, 0, 21))
30+
>c : Symbol(c, Decl(optionalChainWithInstantiationExpression1.ts, 5, 1))
31+
>d : Symbol(A.b.d, Decl(optionalChainWithInstantiationExpression1.ts, 1, 23))
32+
33+
a?.b.d
34+
>a?.b.d : Symbol(A.b.d, Decl(optionalChainWithInstantiationExpression1.ts, 1, 23))
35+
>a?.b : Symbol(A.b, Decl(optionalChainWithInstantiationExpression1.ts, 0, 21))
36+
>a : Symbol(a, Decl(optionalChainWithInstantiationExpression1.ts, 9, 13))
37+
>b : Symbol(A.b, Decl(optionalChainWithInstantiationExpression1.ts, 0, 21))
38+
>d : Symbol(A.b.d, Decl(optionalChainWithInstantiationExpression1.ts, 1, 23))
39+
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
=== tests/cases/compiler/optionalChainWithInstantiationExpression1.ts ===
2+
declare namespace A {
3+
>A : typeof A
4+
5+
export class b<T> {
6+
>b : b<T>
7+
8+
static d: number;
9+
>d : number
10+
11+
constructor(x: T);
12+
>x : T
13+
}
14+
}
15+
16+
type c = unknown;
17+
>c : unknown
18+
19+
declare const a: typeof A | undefined;
20+
>a : typeof A
21+
>A : typeof A
22+
23+
a?.b<c>.d;
24+
>a?.b<c>.d : number
25+
>a?.b<c> : { new (x: unknown): A.b<unknown>; prototype: A.b<any>; d: number; }
26+
>a?.b : typeof A.b
27+
>a : typeof A
28+
>b : typeof A.b
29+
>d : number
30+
31+
a?.b.d
32+
>a?.b.d : number
33+
>a?.b : typeof A.b
34+
>a : typeof A
35+
>b : typeof A.b
36+
>d : number
37+
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
//// [optionalChainWithInstantiationExpression2.ts]
2+
declare interface A {
3+
c: number;
4+
<T>(): T;
5+
}
6+
7+
type b = 'b type';
8+
9+
declare const a: A | undefined;
10+
11+
a?.<b>();
12+
13+
a<b>?.();
14+
15+
16+
//// [optionalChainWithInstantiationExpression2.js]
17+
var _a;
18+
a === null || a === void 0 ? void 0 : a();
19+
(_a = (a)) === null || _a === void 0 ? void 0 : _a();
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
=== tests/cases/compiler/optionalChainWithInstantiationExpression2.ts ===
2+
declare interface A {
3+
>A : Symbol(A, Decl(optionalChainWithInstantiationExpression2.ts, 0, 0))
4+
5+
c: number;
6+
>c : Symbol(A.c, Decl(optionalChainWithInstantiationExpression2.ts, 0, 21))
7+
8+
<T>(): T;
9+
>T : Symbol(T, Decl(optionalChainWithInstantiationExpression2.ts, 2, 5))
10+
>T : Symbol(T, Decl(optionalChainWithInstantiationExpression2.ts, 2, 5))
11+
}
12+
13+
type b = 'b type';
14+
>b : Symbol(b, Decl(optionalChainWithInstantiationExpression2.ts, 3, 1))
15+
16+
declare const a: A | undefined;
17+
>a : Symbol(a, Decl(optionalChainWithInstantiationExpression2.ts, 7, 13))
18+
>A : Symbol(A, Decl(optionalChainWithInstantiationExpression2.ts, 0, 0))
19+
20+
a?.<b>();
21+
>a : Symbol(a, Decl(optionalChainWithInstantiationExpression2.ts, 7, 13))
22+
>b : Symbol(b, Decl(optionalChainWithInstantiationExpression2.ts, 3, 1))
23+
24+
a<b>?.();
25+
>a : Symbol(a, Decl(optionalChainWithInstantiationExpression2.ts, 7, 13))
26+
>b : Symbol(b, Decl(optionalChainWithInstantiationExpression2.ts, 3, 1))
27+
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
=== tests/cases/compiler/optionalChainWithInstantiationExpression2.ts ===
2+
declare interface A {
3+
c: number;
4+
>c : number
5+
6+
<T>(): T;
7+
}
8+
9+
type b = 'b type';
10+
>b : "b type"
11+
12+
declare const a: A | undefined;
13+
>a : A
14+
15+
a?.<b>();
16+
>a?.<b>() : "b type"
17+
>a : A
18+
19+
a<b>?.();
20+
>a<b>?.() : "b type"
21+
>a<b> : { (): "b type"; c: number; }
22+
>a : A
23+

0 commit comments

Comments
 (0)