Skip to content

Commit 8a82395

Browse files
committed
Disallow expression with type parameters as left side of property access (microsoft#49464)
1 parent 76401ad commit 8a82395

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
@@ -1473,6 +1473,10 @@
14731473
"category": "Message",
14741474
"code": 1476
14751475
},
1476+
"An instantiation expression cannot be followed by a property access.": {
1477+
"category": "Error",
1478+
"code": 1477
1479+
},
14761480

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

src/compiler/parser.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5560,6 +5560,11 @@ namespace ts {
55605560
if (isOptionalChain && isPrivateIdentifier(propertyAccess.name)) {
55615561
parseErrorAtRange(propertyAccess.name, Diagnostics.An_optional_chain_cannot_contain_private_identifiers);
55625562
}
5563+
if (isExpressionWithTypeArguments(expression) && expression.typeArguments) {
5564+
const pos = expression.typeArguments.pos - 1;
5565+
const end = skipTrivia(sourceText, expression.typeArguments.end) + 1;
5566+
parseErrorAt(pos, end, Diagnostics.An_instantiation_expression_cannot_be_followed_by_a_property_access);
5567+
}
55635568
return finishNode(propertyAccess, pos);
55645569
}
55655570

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'.
@@ -41,15 +43,19 @@ tests/cases/conformance/types/typeParameters/typeArgumentLists/instantiationExpr
4143
tests/cases/conformance/types/typeParameters/typeArgumentLists/instantiationExpressionErrors.ts(86,15): error TS1005: ';' expected.
4244

4345

44-
==== tests/cases/conformance/types/typeParameters/typeArgumentLists/instantiationExpressionErrors.ts (41 errors) ====
46+
==== tests/cases/conformance/types/typeParameters/typeArgumentLists/instantiationExpressionErrors.ts (43 errors) ====
4547
declare let f: { <T>(): T, g<U>(): U };
4648

4749
// Type arguments in member expressions
4850

4951
const a1 = f<number>; // { (): number; g<U>(): U; }
5052
const a2 = f.g<number>; // () => number
5153
const a3 = f<number>.g; // <U>() => U
54+
~~~~~~~~
55+
!!! error TS1477: An instantiation expression cannot be followed by a property access.
5256
const a4 = f<number>.g<number>; // () => number
57+
~~~~~~~~
58+
!!! error TS1477: An instantiation expression cannot be followed by a property access.
5359
const a5 = f['g']<number>; // () => number
5460

5561
// `[` 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)