Skip to content

Commit

Permalink
Disallow expression with type parameters as left side of property acc…
Browse files Browse the repository at this point in the history
…ess (#49464)
  • Loading branch information
jakebailey authored Jun 24, 2022
1 parent ad6d086 commit 569cdf1
Show file tree
Hide file tree
Showing 22 changed files with 433 additions and 5 deletions.
4 changes: 4 additions & 0 deletions src/compiler/diagnosticMessages.json
Original file line number Diff line number Diff line change
Expand Up @@ -1493,6 +1493,10 @@
"category": "Message",
"code": 1476
},
"An instantiation expression cannot be followed by a property access.": {
"category": "Error",
"code": 1477
},

"The types of '{0}' are incompatible between these types.": {
"category": "Error",
Expand Down
5 changes: 5 additions & 0 deletions src/compiler/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5646,6 +5646,11 @@ namespace ts {
if (isOptionalChain && isPrivateIdentifier(propertyAccess.name)) {
parseErrorAtRange(propertyAccess.name, Diagnostics.An_optional_chain_cannot_contain_private_identifiers);
}
if (isExpressionWithTypeArguments(expression) && expression.typeArguments) {
const pos = expression.typeArguments.pos - 1;
const end = skipTrivia(sourceText, expression.typeArguments.end) + 1;
parseErrorAt(pos, end, Diagnostics.An_instantiation_expression_cannot_be_followed_by_a_property_access);
}
return finishNode(propertyAccess, pos);
}

Expand Down
5 changes: 4 additions & 1 deletion tests/baselines/reference/genericCallWithoutArgs.errors.txt
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
tests/cases/compiler/genericCallWithoutArgs.ts(4,2): error TS1477: An instantiation expression cannot be followed by a property access.
tests/cases/compiler/genericCallWithoutArgs.ts(4,18): error TS1003: Identifier expected.


==== tests/cases/compiler/genericCallWithoutArgs.ts (1 errors) ====
==== tests/cases/compiler/genericCallWithoutArgs.ts (2 errors) ====
function f<X, Y>(x: X, y: Y) {
}

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

!!! error TS1003: Identifier expected.
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
tests/cases/conformance/types/typeParameters/typeArgumentLists/instantiationExpressionErrors.ts(7,13): error TS1477: An instantiation expression cannot be followed by a property access.
tests/cases/conformance/types/typeParameters/typeArgumentLists/instantiationExpressionErrors.ts(8,13): error TS1477: An instantiation expression cannot be followed by a property access.
tests/cases/conformance/types/typeParameters/typeArgumentLists/instantiationExpressionErrors.ts(13,12): error TS2365: Operator '>' cannot be applied to types 'boolean' and 'string[]'.
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.
tests/cases/conformance/types/typeParameters/typeArgumentLists/instantiationExpressionErrors.ts(18,12): error TS2365: Operator '>' cannot be applied to types 'boolean' and 'number'.
Expand All @@ -14,15 +16,19 @@ tests/cases/conformance/types/typeParameters/typeArgumentLists/instantiationExpr
tests/cases/conformance/types/typeParameters/typeArgumentLists/instantiationExpressionErrors.ts(45,12): error TS2365: Operator '>' cannot be applied to types 'boolean' and 'number'.


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

// Type arguments in member expressions

const a1 = f<number>; // { (): number; g<U>(): U; }
const a2 = f.g<number>; // () => number
const a3 = f<number>.g; // <U>() => U
~~~~~~~~
!!! error TS1477: An instantiation expression cannot be followed by a property access.
const a4 = f<number>.g<number>; // () => number
~~~~~~~~
!!! error TS1477: An instantiation expression cannot be followed by a property access.
const a5 = f['g']<number>; // () => number

// `[` is an expression starter and cannot immediately follow a type argument list
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
tests/cases/compiler/optionalChainWithInstantiationExpression1.ts(12,5): error TS1477: An instantiation expression cannot be followed by a property access.


==== tests/cases/compiler/optionalChainWithInstantiationExpression1.ts (1 errors) ====
declare namespace A {
export class b<T> {
static d: number;
constructor(x: T);
}
}

type c = unknown;

declare const a: typeof A | undefined;

a?.b<c>.d;
~~~
!!! error TS1477: An instantiation expression cannot be followed by a property access.

a?.b.d

Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
//// [optionalChainWithInstantiationExpression1.ts]
declare namespace A {
export class b<T> {
static d: number;
constructor(x: T);
}
}

type c = unknown;

declare const a: typeof A | undefined;

a?.b<c>.d;

a?.b.d


//// [optionalChainWithInstantiationExpression1.js]
(a === null || a === void 0 ? void 0 : a.b).d;
a === null || a === void 0 ? void 0 : a.b.d;
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
=== tests/cases/compiler/optionalChainWithInstantiationExpression1.ts ===
declare namespace A {
>A : Symbol(A, Decl(optionalChainWithInstantiationExpression1.ts, 0, 0))

export class b<T> {
>b : Symbol(b, Decl(optionalChainWithInstantiationExpression1.ts, 0, 21))
>T : Symbol(T, Decl(optionalChainWithInstantiationExpression1.ts, 1, 19))

static d: number;
>d : Symbol(b.d, Decl(optionalChainWithInstantiationExpression1.ts, 1, 23))

constructor(x: T);
>x : Symbol(x, Decl(optionalChainWithInstantiationExpression1.ts, 3, 20))
>T : Symbol(T, Decl(optionalChainWithInstantiationExpression1.ts, 1, 19))
}
}

type c = unknown;
>c : Symbol(c, Decl(optionalChainWithInstantiationExpression1.ts, 5, 1))

declare const a: typeof A | undefined;
>a : Symbol(a, Decl(optionalChainWithInstantiationExpression1.ts, 9, 13))
>A : Symbol(A, Decl(optionalChainWithInstantiationExpression1.ts, 0, 0))

a?.b<c>.d;
>a?.b<c>.d : Symbol(A.b.d, Decl(optionalChainWithInstantiationExpression1.ts, 1, 23))
>a?.b : Symbol(A.b, Decl(optionalChainWithInstantiationExpression1.ts, 0, 21))
>a : Symbol(a, Decl(optionalChainWithInstantiationExpression1.ts, 9, 13))
>b : Symbol(A.b, Decl(optionalChainWithInstantiationExpression1.ts, 0, 21))
>c : Symbol(c, Decl(optionalChainWithInstantiationExpression1.ts, 5, 1))
>d : Symbol(A.b.d, Decl(optionalChainWithInstantiationExpression1.ts, 1, 23))

a?.b.d
>a?.b.d : Symbol(A.b.d, Decl(optionalChainWithInstantiationExpression1.ts, 1, 23))
>a?.b : Symbol(A.b, Decl(optionalChainWithInstantiationExpression1.ts, 0, 21))
>a : Symbol(a, Decl(optionalChainWithInstantiationExpression1.ts, 9, 13))
>b : Symbol(A.b, Decl(optionalChainWithInstantiationExpression1.ts, 0, 21))
>d : Symbol(A.b.d, Decl(optionalChainWithInstantiationExpression1.ts, 1, 23))

Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
=== tests/cases/compiler/optionalChainWithInstantiationExpression1.ts ===
declare namespace A {
>A : typeof A

export class b<T> {
>b : b<T>

static d: number;
>d : number

constructor(x: T);
>x : T
}
}

type c = unknown;
>c : unknown

declare const a: typeof A | undefined;
>a : typeof A
>A : typeof A

a?.b<c>.d;
>a?.b<c>.d : number
>a?.b<c> : { new (x: unknown): A.b<unknown>; prototype: A.b<any>; d: number; }
>a?.b : typeof A.b
>a : typeof A
>b : typeof A.b
>d : number

a?.b.d
>a?.b.d : number
>a?.b : typeof A.b
>a : typeof A
>b : typeof A.b
>d : number

Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
tests/cases/compiler/optionalChainWithInstantiationExpression1.ts(12,5): error TS1477: An instantiation expression cannot be followed by a property access.


==== tests/cases/compiler/optionalChainWithInstantiationExpression1.ts (1 errors) ====
declare namespace A {
export class b<T> {
static d: number;
constructor(x: T);
}
}

type c = unknown;

declare const a: typeof A | undefined;

a?.b<c>.d;
~~~
!!! error TS1477: An instantiation expression cannot be followed by a property access.

a?.b.d

Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
//// [optionalChainWithInstantiationExpression1.ts]
declare namespace A {
export class b<T> {
static d: number;
constructor(x: T);
}
}

type c = unknown;

declare const a: typeof A | undefined;

a?.b<c>.d;

a?.b.d


//// [optionalChainWithInstantiationExpression1.js]
a?.b.d;
a?.b.d;
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
=== tests/cases/compiler/optionalChainWithInstantiationExpression1.ts ===
declare namespace A {
>A : Symbol(A, Decl(optionalChainWithInstantiationExpression1.ts, 0, 0))

export class b<T> {
>b : Symbol(b, Decl(optionalChainWithInstantiationExpression1.ts, 0, 21))
>T : Symbol(T, Decl(optionalChainWithInstantiationExpression1.ts, 1, 19))

static d: number;
>d : Symbol(b.d, Decl(optionalChainWithInstantiationExpression1.ts, 1, 23))

constructor(x: T);
>x : Symbol(x, Decl(optionalChainWithInstantiationExpression1.ts, 3, 20))
>T : Symbol(T, Decl(optionalChainWithInstantiationExpression1.ts, 1, 19))
}
}

type c = unknown;
>c : Symbol(c, Decl(optionalChainWithInstantiationExpression1.ts, 5, 1))

declare const a: typeof A | undefined;
>a : Symbol(a, Decl(optionalChainWithInstantiationExpression1.ts, 9, 13))
>A : Symbol(A, Decl(optionalChainWithInstantiationExpression1.ts, 0, 0))

a?.b<c>.d;
>a?.b<c>.d : Symbol(A.b.d, Decl(optionalChainWithInstantiationExpression1.ts, 1, 23))
>a?.b : Symbol(A.b, Decl(optionalChainWithInstantiationExpression1.ts, 0, 21))
>a : Symbol(a, Decl(optionalChainWithInstantiationExpression1.ts, 9, 13))
>b : Symbol(A.b, Decl(optionalChainWithInstantiationExpression1.ts, 0, 21))
>c : Symbol(c, Decl(optionalChainWithInstantiationExpression1.ts, 5, 1))
>d : Symbol(A.b.d, Decl(optionalChainWithInstantiationExpression1.ts, 1, 23))

a?.b.d
>a?.b.d : Symbol(A.b.d, Decl(optionalChainWithInstantiationExpression1.ts, 1, 23))
>a?.b : Symbol(A.b, Decl(optionalChainWithInstantiationExpression1.ts, 0, 21))
>a : Symbol(a, Decl(optionalChainWithInstantiationExpression1.ts, 9, 13))
>b : Symbol(A.b, Decl(optionalChainWithInstantiationExpression1.ts, 0, 21))
>d : Symbol(A.b.d, Decl(optionalChainWithInstantiationExpression1.ts, 1, 23))

Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
=== tests/cases/compiler/optionalChainWithInstantiationExpression1.ts ===
declare namespace A {
>A : typeof A

export class b<T> {
>b : b<T>

static d: number;
>d : number

constructor(x: T);
>x : T
}
}

type c = unknown;
>c : unknown

declare const a: typeof A | undefined;
>a : typeof A
>A : typeof A

a?.b<c>.d;
>a?.b<c>.d : number
>a?.b<c> : { new (x: unknown): A.b<unknown>; prototype: A.b<any>; d: number; }
>a?.b : typeof A.b
>a : typeof A
>b : typeof A.b
>d : number

a?.b.d
>a?.b.d : number
>a?.b : typeof A.b
>a : typeof A
>b : typeof A.b
>d : number

Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
//// [optionalChainWithInstantiationExpression2.ts]
declare interface A {
c: number;
<T>(): T;
}

type b = 'b type';

declare const a: A | undefined;

a?.<b>();

a<b>?.();


//// [optionalChainWithInstantiationExpression2.js]
var _a;
a === null || a === void 0 ? void 0 : a();
(_a = (a)) === null || _a === void 0 ? void 0 : _a();
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
=== tests/cases/compiler/optionalChainWithInstantiationExpression2.ts ===
declare interface A {
>A : Symbol(A, Decl(optionalChainWithInstantiationExpression2.ts, 0, 0))

c: number;
>c : Symbol(A.c, Decl(optionalChainWithInstantiationExpression2.ts, 0, 21))

<T>(): T;
>T : Symbol(T, Decl(optionalChainWithInstantiationExpression2.ts, 2, 5))
>T : Symbol(T, Decl(optionalChainWithInstantiationExpression2.ts, 2, 5))
}

type b = 'b type';
>b : Symbol(b, Decl(optionalChainWithInstantiationExpression2.ts, 3, 1))

declare const a: A | undefined;
>a : Symbol(a, Decl(optionalChainWithInstantiationExpression2.ts, 7, 13))
>A : Symbol(A, Decl(optionalChainWithInstantiationExpression2.ts, 0, 0))

a?.<b>();
>a : Symbol(a, Decl(optionalChainWithInstantiationExpression2.ts, 7, 13))
>b : Symbol(b, Decl(optionalChainWithInstantiationExpression2.ts, 3, 1))

a<b>?.();
>a : Symbol(a, Decl(optionalChainWithInstantiationExpression2.ts, 7, 13))
>b : Symbol(b, Decl(optionalChainWithInstantiationExpression2.ts, 3, 1))

Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
=== tests/cases/compiler/optionalChainWithInstantiationExpression2.ts ===
declare interface A {
c: number;
>c : number

<T>(): T;
}

type b = 'b type';
>b : "b type"

declare const a: A | undefined;
>a : A

a?.<b>();
>a?.<b>() : "b type"
>a : A

a<b>?.();
>a<b>?.() : "b type"
>a<b> : { (): "b type"; c: number; }
>a : A

Loading

0 comments on commit 569cdf1

Please sign in to comment.