From eb231ba0ce49ef427cf1edeeff37dc8950b0f37a Mon Sep 17 00:00:00 2001 From: uhyo Date: Sun, 12 Jul 2020 13:35:47 +0900 Subject: [PATCH 01/10] add graceful error message for unparenthesized function types in union and intersection --- src/compiler/diagnosticMessages.json | 16 +++++++++++ src/compiler/parser.ts | 40 ++++++++++++++++++++++++---- 2 files changed, 51 insertions(+), 5 deletions(-) diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index cc5ed962ab91a..657302401e18a 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -1164,6 +1164,22 @@ "category": "Error", "code": 1384 }, + "Function type notation must be parenthesized when used in a union type.": { + "category": "Error", + "code": 1385 + }, + "Constructor type notation must be parenthesized when used in a union type.": { + "category": "Error", + "code": 1386 + }, + "Function type notation must be parenthesized when used in an intersection type.": { + "category": "Error", + "code": 1387 + }, + "Constructor type notation must be parenthesized when used in an intersection type.": { + "category": "Error", + "code": 1388 + }, "The types of '{0}' are incompatible between these types.": { "category": "Error", diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 8f7ea52ebe94b..26628f1ae0d1d 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -3569,18 +3569,36 @@ namespace ts { return parsePostfixTypeOrHigher(); } + function parseFunctionOrConstructorTypeToError( + functionTypeDiagnostic: DiagnosticMessage, + constructorTypeDiagnostic: DiagnosticMessage + ): TypeNode | undefined { + // function type notations or constructor type notations are not allowed in this context, + // but we try parsing them for graceful error message + if (isStartOfFunctionType() || token() === SyntaxKind.NewKeyword) { + const type = parseFunctionOrConstructorType(); + parseErrorAtRange(type, isFunctionTypeNode(type) ? functionTypeDiagnostic : constructorTypeDiagnostic); + return type; + } + return undefined; + } + function parseUnionOrIntersectionType( operator: SyntaxKind.BarToken | SyntaxKind.AmpersandToken, parseConstituentType: () => TypeNode, - createTypeNode: (types: NodeArray) => UnionOrIntersectionTypeNode + createTypeNode: (types: NodeArray) => UnionOrIntersectionTypeNode, + functionTypeDiagnostic: DiagnosticMessage, + constructorTypeDiagnostic: DiagnosticMessage ): TypeNode { const pos = getNodePos(); const hasLeadingOperator = parseOptional(operator); - let type = parseConstituentType(); + let type = hasLeadingOperator ? + parseFunctionOrConstructorTypeToError(functionTypeDiagnostic, constructorTypeDiagnostic) || parseConstituentType() : + parseConstituentType(); if (token() === operator || hasLeadingOperator) { const types = [type]; while (parseOptional(operator)) { - types.push(parseConstituentType()); + types.push(parseFunctionOrConstructorTypeToError(functionTypeDiagnostic, constructorTypeDiagnostic) || parseConstituentType()); } type = finishNode(createTypeNode(createNodeArray(types, pos)), pos); } @@ -3588,11 +3606,23 @@ namespace ts { } function parseIntersectionTypeOrHigher(): TypeNode { - return parseUnionOrIntersectionType(SyntaxKind.AmpersandToken, parseTypeOperatorOrHigher, factory.createIntersectionTypeNode); + return parseUnionOrIntersectionType( + SyntaxKind.AmpersandToken, + parseTypeOperatorOrHigher, + factory.createIntersectionTypeNode, + Diagnostics.Function_type_notation_must_be_parenthesized_when_used_in_an_intersection_type, + Diagnostics.Constructor_type_notation_must_be_parenthesized_when_used_in_an_intersection_type + ); } function parseUnionTypeOrHigher(): TypeNode { - return parseUnionOrIntersectionType(SyntaxKind.BarToken, parseIntersectionTypeOrHigher, factory.createUnionTypeNode); + return parseUnionOrIntersectionType( + SyntaxKind.BarToken, + parseIntersectionTypeOrHigher, + factory.createUnionTypeNode, + Diagnostics.Function_type_notation_must_be_parenthesized_when_used_in_a_union_type, + Diagnostics.Constructor_type_notation_must_be_parenthesized_when_used_in_a_union_type + ); } function isStartOfFunctionType(): boolean { From 6774f90c8c1b1440860d5612f97636c86ee58f5f Mon Sep 17 00:00:00 2001 From: uhyo Date: Sun, 12 Jul 2020 16:46:17 +0900 Subject: [PATCH 02/10] add unparenthesizedFunctionTypeInUnionOrIntersection test --- ...nctionTypeInUnionOrIntersection.errors.txt | 80 +++++++++++++++++++ ...esizedFunctionTypeInUnionOrIntersection.js | 31 +++++++ ...dFunctionTypeInUnionOrIntersection.symbols | 76 ++++++++++++++++++ ...zedFunctionTypeInUnionOrIntersection.types | 77 ++++++++++++++++++ ...esizedFunctionTypeInUnionOrIntersection.ts | 27 +++++++ 5 files changed, 291 insertions(+) create mode 100644 tests/baselines/reference/unparenthesizedFunctionTypeInUnionOrIntersection.errors.txt create mode 100644 tests/baselines/reference/unparenthesizedFunctionTypeInUnionOrIntersection.js create mode 100644 tests/baselines/reference/unparenthesizedFunctionTypeInUnionOrIntersection.symbols create mode 100644 tests/baselines/reference/unparenthesizedFunctionTypeInUnionOrIntersection.types create mode 100644 tests/cases/compiler/unparenthesizedFunctionTypeInUnionOrIntersection.ts diff --git a/tests/baselines/reference/unparenthesizedFunctionTypeInUnionOrIntersection.errors.txt b/tests/baselines/reference/unparenthesizedFunctionTypeInUnionOrIntersection.errors.txt new file mode 100644 index 0000000000000..b6e08947b612b --- /dev/null +++ b/tests/baselines/reference/unparenthesizedFunctionTypeInUnionOrIntersection.errors.txt @@ -0,0 +1,80 @@ +tests/cases/compiler/unparenthesizedFunctionTypeInUnionOrIntersection.ts(1,19): error TS1385: Function type notation must be parenthesized when used in a union type. +tests/cases/compiler/unparenthesizedFunctionTypeInUnionOrIntersection.ts(2,19): error TS1385: Function type notation must be parenthesized when used in a union type. +tests/cases/compiler/unparenthesizedFunctionTypeInUnionOrIntersection.ts(3,12): error TS1385: Function type notation must be parenthesized when used in a union type. +tests/cases/compiler/unparenthesizedFunctionTypeInUnionOrIntersection.ts(4,12): error TS1385: Function type notation must be parenthesized when used in a union type. +tests/cases/compiler/unparenthesizedFunctionTypeInUnionOrIntersection.ts(5,19): error TS1385: Function type notation must be parenthesized when used in a union type. +tests/cases/compiler/unparenthesizedFunctionTypeInUnionOrIntersection.ts(8,4): error TS1385: Function type notation must be parenthesized when used in a union type. +tests/cases/compiler/unparenthesizedFunctionTypeInUnionOrIntersection.ts(11,19): error TS1387: Function type notation must be parenthesized when used in an intersection type. +tests/cases/compiler/unparenthesizedFunctionTypeInUnionOrIntersection.ts(12,19): error TS1387: Function type notation must be parenthesized when used in an intersection type. +tests/cases/compiler/unparenthesizedFunctionTypeInUnionOrIntersection.ts(13,12): error TS1387: Function type notation must be parenthesized when used in an intersection type. +tests/cases/compiler/unparenthesizedFunctionTypeInUnionOrIntersection.ts(14,12): error TS1387: Function type notation must be parenthesized when used in an intersection type. +tests/cases/compiler/unparenthesizedFunctionTypeInUnionOrIntersection.ts(15,19): error TS1387: Function type notation must be parenthesized when used in an intersection type. +tests/cases/compiler/unparenthesizedFunctionTypeInUnionOrIntersection.ts(18,4): error TS1387: Function type notation must be parenthesized when used in an intersection type. +tests/cases/compiler/unparenthesizedFunctionTypeInUnionOrIntersection.ts(20,37): error TS1385: Function type notation must be parenthesized when used in a union type. +tests/cases/compiler/unparenthesizedFunctionTypeInUnionOrIntersection.ts(21,31): error TS1387: Function type notation must be parenthesized when used in an intersection type. +tests/cases/compiler/unparenthesizedFunctionTypeInUnionOrIntersection.ts(22,16): error TS1387: Function type notation must be parenthesized when used in an intersection type. +tests/cases/compiler/unparenthesizedFunctionTypeInUnionOrIntersection.ts(22,37): error TS1385: Function type notation must be parenthesized when used in a union type. + + +==== tests/cases/compiler/unparenthesizedFunctionTypeInUnionOrIntersection.ts (16 errors) ==== + type U1 = string | () => void; + ~~~~~~~~~~~ +!!! error TS1385: Function type notation must be parenthesized when used in a union type. + type U2 = string | (foo: number) => void + ~~~~~~~~~~~~~~~~~~~~~~ +!!! error TS1385: Function type notation must be parenthesized when used in a union type. + type U3 = | () => number + ~~~~~~~~~~~~~ +!!! error TS1385: Function type notation must be parenthesized when used in a union type. + type U4 = | (foo?: number) => void; + ~~~~~~~~~~~~~~~~~~~~~~~ +!!! error TS1385: Function type notation must be parenthesized when used in a union type. + type U5 = string | (number: number, foo?: string) => void | number; + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +!!! error TS1385: Function type notation must be parenthesized when used in a union type. + type U6 = + | string + | (...args: any[]) => void + ~~~~~~~~~~~~~~~~~~~~~~~~~ + | number; + ~~~~~~~~~~ +!!! error TS1385: Function type notation must be parenthesized when used in a union type. + + type I1 = string & () => void; + ~~~~~~~~~~~ +!!! error TS1387: Function type notation must be parenthesized when used in an intersection type. + type I2 = string & (...foo: number[]) => void; + ~~~~~~~~~~~~~~~~~~~~~~~~~~~ +!!! error TS1387: Function type notation must be parenthesized when used in an intersection type. + type I3 = & () => boolean + ~~~~~~~~~~~~~~ +!!! error TS1387: Function type notation must be parenthesized when used in an intersection type. + type I4 = & () => boolean & null; + ~~~~~~~~~~~~~~~~~~~~~ +!!! error TS1387: Function type notation must be parenthesized when used in an intersection type. + type I5 = string & (any: any, any2: any) => any & any; + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +!!! error TS1387: Function type notation must be parenthesized when used in an intersection type. + type I6 = + & string + & (foo: any) => void; + ~~~~~~~~~~~~~~~~~~~ +!!! error TS1387: Function type notation must be parenthesized when used in an intersection type. + + type M1 = string | number & string | () => number; + ~~~~~~~~~~~~~ +!!! error TS1385: Function type notation must be parenthesized when used in a union type. + type M2 = any & string | any & () => void; + ~~~~~~~~~~~ +!!! error TS1387: Function type notation must be parenthesized when used in an intersection type. + type M3 = any & (foo: any) => void | () => void & any; + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +!!! error TS1387: Function type notation must be parenthesized when used in an intersection type. + ~~~~~~~~~~~~~~~~~ +!!! error TS1385: Function type notation must be parenthesized when used in a union type. + + type OK1 = string | (number); + type OK2 = string | ((number)); + type OK3 = string | (()=> void); + type OK4 = string | (()=> string | number); + \ No newline at end of file diff --git a/tests/baselines/reference/unparenthesizedFunctionTypeInUnionOrIntersection.js b/tests/baselines/reference/unparenthesizedFunctionTypeInUnionOrIntersection.js new file mode 100644 index 0000000000000..8e9132059575a --- /dev/null +++ b/tests/baselines/reference/unparenthesizedFunctionTypeInUnionOrIntersection.js @@ -0,0 +1,31 @@ +//// [unparenthesizedFunctionTypeInUnionOrIntersection.ts] +type U1 = string | () => void; +type U2 = string | (foo: number) => void +type U3 = | () => number +type U4 = | (foo?: number) => void; +type U5 = string | (number: number, foo?: string) => void | number; +type U6 = + | string + | (...args: any[]) => void + | number; + +type I1 = string & () => void; +type I2 = string & (...foo: number[]) => void; +type I3 = & () => boolean +type I4 = & () => boolean & null; +type I5 = string & (any: any, any2: any) => any & any; +type I6 = + & string + & (foo: any) => void; + +type M1 = string | number & string | () => number; +type M2 = any & string | any & () => void; +type M3 = any & (foo: any) => void | () => void & any; + +type OK1 = string | (number); +type OK2 = string | ((number)); +type OK3 = string | (()=> void); +type OK4 = string | (()=> string | number); + + +//// [unparenthesizedFunctionTypeInUnionOrIntersection.js] diff --git a/tests/baselines/reference/unparenthesizedFunctionTypeInUnionOrIntersection.symbols b/tests/baselines/reference/unparenthesizedFunctionTypeInUnionOrIntersection.symbols new file mode 100644 index 0000000000000..521f10a322e18 --- /dev/null +++ b/tests/baselines/reference/unparenthesizedFunctionTypeInUnionOrIntersection.symbols @@ -0,0 +1,76 @@ +=== tests/cases/compiler/unparenthesizedFunctionTypeInUnionOrIntersection.ts === +type U1 = string | () => void; +>U1 : Symbol(U1, Decl(unparenthesizedFunctionTypeInUnionOrIntersection.ts, 0, 0)) + +type U2 = string | (foo: number) => void +>U2 : Symbol(U2, Decl(unparenthesizedFunctionTypeInUnionOrIntersection.ts, 0, 30)) +>foo : Symbol(foo, Decl(unparenthesizedFunctionTypeInUnionOrIntersection.ts, 1, 20)) + +type U3 = | () => number +>U3 : Symbol(U3, Decl(unparenthesizedFunctionTypeInUnionOrIntersection.ts, 1, 40)) + +type U4 = | (foo?: number) => void; +>U4 : Symbol(U4, Decl(unparenthesizedFunctionTypeInUnionOrIntersection.ts, 2, 24)) +>foo : Symbol(foo, Decl(unparenthesizedFunctionTypeInUnionOrIntersection.ts, 3, 13)) + +type U5 = string | (number: number, foo?: string) => void | number; +>U5 : Symbol(U5, Decl(unparenthesizedFunctionTypeInUnionOrIntersection.ts, 3, 35)) +>number : Symbol(number, Decl(unparenthesizedFunctionTypeInUnionOrIntersection.ts, 4, 20)) +>foo : Symbol(foo, Decl(unparenthesizedFunctionTypeInUnionOrIntersection.ts, 4, 35)) + +type U6 = +>U6 : Symbol(U6, Decl(unparenthesizedFunctionTypeInUnionOrIntersection.ts, 4, 67)) + + | string + | (...args: any[]) => void +>args : Symbol(args, Decl(unparenthesizedFunctionTypeInUnionOrIntersection.ts, 7, 5)) + + | number; + +type I1 = string & () => void; +>I1 : Symbol(I1, Decl(unparenthesizedFunctionTypeInUnionOrIntersection.ts, 8, 11)) + +type I2 = string & (...foo: number[]) => void; +>I2 : Symbol(I2, Decl(unparenthesizedFunctionTypeInUnionOrIntersection.ts, 10, 30)) +>foo : Symbol(foo, Decl(unparenthesizedFunctionTypeInUnionOrIntersection.ts, 11, 20)) + +type I3 = & () => boolean +>I3 : Symbol(I3, Decl(unparenthesizedFunctionTypeInUnionOrIntersection.ts, 11, 46)) + +type I4 = & () => boolean & null; +>I4 : Symbol(I4, Decl(unparenthesizedFunctionTypeInUnionOrIntersection.ts, 12, 25)) + +type I5 = string & (any: any, any2: any) => any & any; +>I5 : Symbol(I5, Decl(unparenthesizedFunctionTypeInUnionOrIntersection.ts, 13, 33)) +>any : Symbol(any, Decl(unparenthesizedFunctionTypeInUnionOrIntersection.ts, 14, 20)) +>any2 : Symbol(any2, Decl(unparenthesizedFunctionTypeInUnionOrIntersection.ts, 14, 29)) + +type I6 = +>I6 : Symbol(I6, Decl(unparenthesizedFunctionTypeInUnionOrIntersection.ts, 14, 54)) + + & string + & (foo: any) => void; +>foo : Symbol(foo, Decl(unparenthesizedFunctionTypeInUnionOrIntersection.ts, 17, 5)) + +type M1 = string | number & string | () => number; +>M1 : Symbol(M1, Decl(unparenthesizedFunctionTypeInUnionOrIntersection.ts, 17, 23)) + +type M2 = any & string | any & () => void; +>M2 : Symbol(M2, Decl(unparenthesizedFunctionTypeInUnionOrIntersection.ts, 19, 50)) + +type M3 = any & (foo: any) => void | () => void & any; +>M3 : Symbol(M3, Decl(unparenthesizedFunctionTypeInUnionOrIntersection.ts, 20, 42)) +>foo : Symbol(foo, Decl(unparenthesizedFunctionTypeInUnionOrIntersection.ts, 21, 17)) + +type OK1 = string | (number); +>OK1 : Symbol(OK1, Decl(unparenthesizedFunctionTypeInUnionOrIntersection.ts, 21, 54)) + +type OK2 = string | ((number)); +>OK2 : Symbol(OK2, Decl(unparenthesizedFunctionTypeInUnionOrIntersection.ts, 23, 29)) + +type OK3 = string | (()=> void); +>OK3 : Symbol(OK3, Decl(unparenthesizedFunctionTypeInUnionOrIntersection.ts, 24, 31)) + +type OK4 = string | (()=> string | number); +>OK4 : Symbol(OK4, Decl(unparenthesizedFunctionTypeInUnionOrIntersection.ts, 25, 32)) + diff --git a/tests/baselines/reference/unparenthesizedFunctionTypeInUnionOrIntersection.types b/tests/baselines/reference/unparenthesizedFunctionTypeInUnionOrIntersection.types new file mode 100644 index 0000000000000..40dd867a696a8 --- /dev/null +++ b/tests/baselines/reference/unparenthesizedFunctionTypeInUnionOrIntersection.types @@ -0,0 +1,77 @@ +=== tests/cases/compiler/unparenthesizedFunctionTypeInUnionOrIntersection.ts === +type U1 = string | () => void; +>U1 : U1 + +type U2 = string | (foo: number) => void +>U2 : U2 +>foo : number + +type U3 = | () => number +>U3 : () => number + +type U4 = | (foo?: number) => void; +>U4 : (foo?: number) => void +>foo : number + +type U5 = string | (number: number, foo?: string) => void | number; +>U5 : U5 +>number : number +>foo : string + +type U6 = +>U6 : U6 + + | string + | (...args: any[]) => void +>args : any[] + + | number; + +type I1 = string & () => void; +>I1 : I1 + +type I2 = string & (...foo: number[]) => void; +>I2 : I2 +>foo : number[] + +type I3 = & () => boolean +>I3 : () => boolean + +type I4 = & () => boolean & null; +>I4 : () => boolean & null +>null : null + +type I5 = string & (any: any, any2: any) => any & any; +>I5 : I5 +>any : any +>any2 : any + +type I6 = +>I6 : I6 + + & string + & (foo: any) => void; +>foo : any + +type M1 = string | number & string | () => number; +>M1 : M1 + +type M2 = any & string | any & () => void; +>M2 : any + +type M3 = any & (foo: any) => void | () => void & any; +>M3 : any +>foo : any + +type OK1 = string | (number); +>OK1 : OK1 + +type OK2 = string | ((number)); +>OK2 : OK1 + +type OK3 = string | (()=> void); +>OK3 : OK3 + +type OK4 = string | (()=> string | number); +>OK4 : OK4 + diff --git a/tests/cases/compiler/unparenthesizedFunctionTypeInUnionOrIntersection.ts b/tests/cases/compiler/unparenthesizedFunctionTypeInUnionOrIntersection.ts new file mode 100644 index 0000000000000..861f7e8cc2020 --- /dev/null +++ b/tests/cases/compiler/unparenthesizedFunctionTypeInUnionOrIntersection.ts @@ -0,0 +1,27 @@ +type U1 = string | () => void; +type U2 = string | (foo: number) => void +type U3 = | () => number +type U4 = | (foo?: number) => void; +type U5 = string | (number: number, foo?: string) => void | number; +type U6 = + | string + | (...args: any[]) => void + | number; + +type I1 = string & () => void; +type I2 = string & (...foo: number[]) => void; +type I3 = & () => boolean +type I4 = & () => boolean & null; +type I5 = string & (any: any, any2: any) => any & any; +type I6 = + & string + & (foo: any) => void; + +type M1 = string | number & string | () => number; +type M2 = any & string | any & () => void; +type M3 = any & (foo: any) => void | () => void & any; + +type OK1 = string | (number); +type OK2 = string | ((number)); +type OK3 = string | (()=> void); +type OK4 = string | (()=> string | number); From 50ab58c1a2f92639853e3fed27dd6349fa2df23e Mon Sep 17 00:00:00 2001 From: uhyo Date: Sun, 12 Jul 2020 16:49:12 +0900 Subject: [PATCH 03/10] add unparenthesizedConstructorTypeInUnionOrIntersection test --- ...ructorTypeInUnionOrIntersection.errors.txt | 78 +++++++++++++++++++ ...zedConstructorTypeInUnionOrIntersection.js | 29 +++++++ ...nstructorTypeInUnionOrIntersection.symbols | 70 +++++++++++++++++ ...ConstructorTypeInUnionOrIntersection.types | 71 +++++++++++++++++ ...zedConstructorTypeInUnionOrIntersection.ts | 25 ++++++ 5 files changed, 273 insertions(+) create mode 100644 tests/baselines/reference/unparenthesizedConstructorTypeInUnionOrIntersection.errors.txt create mode 100644 tests/baselines/reference/unparenthesizedConstructorTypeInUnionOrIntersection.js create mode 100644 tests/baselines/reference/unparenthesizedConstructorTypeInUnionOrIntersection.symbols create mode 100644 tests/baselines/reference/unparenthesizedConstructorTypeInUnionOrIntersection.types create mode 100644 tests/cases/compiler/unparenthesizedConstructorTypeInUnionOrIntersection.ts diff --git a/tests/baselines/reference/unparenthesizedConstructorTypeInUnionOrIntersection.errors.txt b/tests/baselines/reference/unparenthesizedConstructorTypeInUnionOrIntersection.errors.txt new file mode 100644 index 0000000000000..df9d2510c60f7 --- /dev/null +++ b/tests/baselines/reference/unparenthesizedConstructorTypeInUnionOrIntersection.errors.txt @@ -0,0 +1,78 @@ +tests/cases/compiler/unparenthesizedConstructorTypeInUnionOrIntersection.ts(1,19): error TS1386: Constructor type notation must be parenthesized when used in a union type. +tests/cases/compiler/unparenthesizedConstructorTypeInUnionOrIntersection.ts(2,19): error TS1386: Constructor type notation must be parenthesized when used in a union type. +tests/cases/compiler/unparenthesizedConstructorTypeInUnionOrIntersection.ts(3,12): error TS1386: Constructor type notation must be parenthesized when used in a union type. +tests/cases/compiler/unparenthesizedConstructorTypeInUnionOrIntersection.ts(4,12): error TS1386: Constructor type notation must be parenthesized when used in a union type. +tests/cases/compiler/unparenthesizedConstructorTypeInUnionOrIntersection.ts(5,19): error TS1386: Constructor type notation must be parenthesized when used in a union type. +tests/cases/compiler/unparenthesizedConstructorTypeInUnionOrIntersection.ts(8,4): error TS1386: Constructor type notation must be parenthesized when used in a union type. +tests/cases/compiler/unparenthesizedConstructorTypeInUnionOrIntersection.ts(11,19): error TS1388: Constructor type notation must be parenthesized when used in an intersection type. +tests/cases/compiler/unparenthesizedConstructorTypeInUnionOrIntersection.ts(12,19): error TS1388: Constructor type notation must be parenthesized when used in an intersection type. +tests/cases/compiler/unparenthesizedConstructorTypeInUnionOrIntersection.ts(13,12): error TS1388: Constructor type notation must be parenthesized when used in an intersection type. +tests/cases/compiler/unparenthesizedConstructorTypeInUnionOrIntersection.ts(14,12): error TS1388: Constructor type notation must be parenthesized when used in an intersection type. +tests/cases/compiler/unparenthesizedConstructorTypeInUnionOrIntersection.ts(15,19): error TS1388: Constructor type notation must be parenthesized when used in an intersection type. +tests/cases/compiler/unparenthesizedConstructorTypeInUnionOrIntersection.ts(18,4): error TS1388: Constructor type notation must be parenthesized when used in an intersection type. +tests/cases/compiler/unparenthesizedConstructorTypeInUnionOrIntersection.ts(20,37): error TS1386: Constructor type notation must be parenthesized when used in a union type. +tests/cases/compiler/unparenthesizedConstructorTypeInUnionOrIntersection.ts(21,31): error TS1388: Constructor type notation must be parenthesized when used in an intersection type. +tests/cases/compiler/unparenthesizedConstructorTypeInUnionOrIntersection.ts(22,16): error TS1388: Constructor type notation must be parenthesized when used in an intersection type. +tests/cases/compiler/unparenthesizedConstructorTypeInUnionOrIntersection.ts(22,41): error TS1386: Constructor type notation must be parenthesized when used in a union type. + + +==== tests/cases/compiler/unparenthesizedConstructorTypeInUnionOrIntersection.ts (16 errors) ==== + type U1 = string | new () => void; + ~~~~~~~~~~~~~~~ +!!! error TS1386: Constructor type notation must be parenthesized when used in a union type. + type U2 = string | new (foo: number) => void + ~~~~~~~~~~~~~~~~~~~~~~~~~~ +!!! error TS1386: Constructor type notation must be parenthesized when used in a union type. + type U3 = | new () => number + ~~~~~~~~~~~~~~~~~ +!!! error TS1386: Constructor type notation must be parenthesized when used in a union type. + type U4 = | new (foo?: number) => void; + ~~~~~~~~~~~~~~~~~~~~~~~~~~~ +!!! error TS1386: Constructor type notation must be parenthesized when used in a union type. + type U5 = string | new (number: number, foo?: string) => void | number; + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +!!! error TS1386: Constructor type notation must be parenthesized when used in a union type. + type U6 = + | string + | new (...args: any[]) => void + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + | number; + ~~~~~~~~~~ +!!! error TS1386: Constructor type notation must be parenthesized when used in a union type. + + type I1 = string & new () => void; + ~~~~~~~~~~~~~~~ +!!! error TS1388: Constructor type notation must be parenthesized when used in an intersection type. + type I2 = string & new (...foo: number[]) => void; + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +!!! error TS1388: Constructor type notation must be parenthesized when used in an intersection type. + type I3 = & new () => boolean + ~~~~~~~~~~~~~~~~~~ +!!! error TS1388: Constructor type notation must be parenthesized when used in an intersection type. + type I4 = & new () => boolean & null; + ~~~~~~~~~~~~~~~~~~~~~~~~~ +!!! error TS1388: Constructor type notation must be parenthesized when used in an intersection type. + type I5 = string & new (any: any, any2: any) => any & any; + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +!!! error TS1388: Constructor type notation must be parenthesized when used in an intersection type. + type I6 = + & string + & new (foo: any) => void; + ~~~~~~~~~~~~~~~~~~~~~~~ +!!! error TS1388: Constructor type notation must be parenthesized when used in an intersection type. + + type M1 = string | number & string | new () => number; + ~~~~~~~~~~~~~~~~~ +!!! error TS1386: Constructor type notation must be parenthesized when used in a union type. + type M2 = any & string | any & new () => void; + ~~~~~~~~~~~~~~~ +!!! error TS1388: Constructor type notation must be parenthesized when used in an intersection type. + type M3 = any & new (foo: any) => void | new () => void & any; + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +!!! error TS1388: Constructor type notation must be parenthesized when used in an intersection type. + ~~~~~~~~~~~~~~~~~~~~~ +!!! error TS1386: Constructor type notation must be parenthesized when used in a union type. + + type OK1 = string | (new ()=> void); + type OK2 = string | (new ()=> string | number); + \ No newline at end of file diff --git a/tests/baselines/reference/unparenthesizedConstructorTypeInUnionOrIntersection.js b/tests/baselines/reference/unparenthesizedConstructorTypeInUnionOrIntersection.js new file mode 100644 index 0000000000000..a16c26e47ac60 --- /dev/null +++ b/tests/baselines/reference/unparenthesizedConstructorTypeInUnionOrIntersection.js @@ -0,0 +1,29 @@ +//// [unparenthesizedConstructorTypeInUnionOrIntersection.ts] +type U1 = string | new () => void; +type U2 = string | new (foo: number) => void +type U3 = | new () => number +type U4 = | new (foo?: number) => void; +type U5 = string | new (number: number, foo?: string) => void | number; +type U6 = + | string + | new (...args: any[]) => void + | number; + +type I1 = string & new () => void; +type I2 = string & new (...foo: number[]) => void; +type I3 = & new () => boolean +type I4 = & new () => boolean & null; +type I5 = string & new (any: any, any2: any) => any & any; +type I6 = + & string + & new (foo: any) => void; + +type M1 = string | number & string | new () => number; +type M2 = any & string | any & new () => void; +type M3 = any & new (foo: any) => void | new () => void & any; + +type OK1 = string | (new ()=> void); +type OK2 = string | (new ()=> string | number); + + +//// [unparenthesizedConstructorTypeInUnionOrIntersection.js] diff --git a/tests/baselines/reference/unparenthesizedConstructorTypeInUnionOrIntersection.symbols b/tests/baselines/reference/unparenthesizedConstructorTypeInUnionOrIntersection.symbols new file mode 100644 index 0000000000000..1719b440df9d0 --- /dev/null +++ b/tests/baselines/reference/unparenthesizedConstructorTypeInUnionOrIntersection.symbols @@ -0,0 +1,70 @@ +=== tests/cases/compiler/unparenthesizedConstructorTypeInUnionOrIntersection.ts === +type U1 = string | new () => void; +>U1 : Symbol(U1, Decl(unparenthesizedConstructorTypeInUnionOrIntersection.ts, 0, 0)) + +type U2 = string | new (foo: number) => void +>U2 : Symbol(U2, Decl(unparenthesizedConstructorTypeInUnionOrIntersection.ts, 0, 34)) +>foo : Symbol(foo, Decl(unparenthesizedConstructorTypeInUnionOrIntersection.ts, 1, 24)) + +type U3 = | new () => number +>U3 : Symbol(U3, Decl(unparenthesizedConstructorTypeInUnionOrIntersection.ts, 1, 44)) + +type U4 = | new (foo?: number) => void; +>U4 : Symbol(U4, Decl(unparenthesizedConstructorTypeInUnionOrIntersection.ts, 2, 28)) +>foo : Symbol(foo, Decl(unparenthesizedConstructorTypeInUnionOrIntersection.ts, 3, 17)) + +type U5 = string | new (number: number, foo?: string) => void | number; +>U5 : Symbol(U5, Decl(unparenthesizedConstructorTypeInUnionOrIntersection.ts, 3, 39)) +>number : Symbol(number, Decl(unparenthesizedConstructorTypeInUnionOrIntersection.ts, 4, 24)) +>foo : Symbol(foo, Decl(unparenthesizedConstructorTypeInUnionOrIntersection.ts, 4, 39)) + +type U6 = +>U6 : Symbol(U6, Decl(unparenthesizedConstructorTypeInUnionOrIntersection.ts, 4, 71)) + + | string + | new (...args: any[]) => void +>args : Symbol(args, Decl(unparenthesizedConstructorTypeInUnionOrIntersection.ts, 7, 9)) + + | number; + +type I1 = string & new () => void; +>I1 : Symbol(I1, Decl(unparenthesizedConstructorTypeInUnionOrIntersection.ts, 8, 11)) + +type I2 = string & new (...foo: number[]) => void; +>I2 : Symbol(I2, Decl(unparenthesizedConstructorTypeInUnionOrIntersection.ts, 10, 34)) +>foo : Symbol(foo, Decl(unparenthesizedConstructorTypeInUnionOrIntersection.ts, 11, 24)) + +type I3 = & new () => boolean +>I3 : Symbol(I3, Decl(unparenthesizedConstructorTypeInUnionOrIntersection.ts, 11, 50)) + +type I4 = & new () => boolean & null; +>I4 : Symbol(I4, Decl(unparenthesizedConstructorTypeInUnionOrIntersection.ts, 12, 29)) + +type I5 = string & new (any: any, any2: any) => any & any; +>I5 : Symbol(I5, Decl(unparenthesizedConstructorTypeInUnionOrIntersection.ts, 13, 37)) +>any : Symbol(any, Decl(unparenthesizedConstructorTypeInUnionOrIntersection.ts, 14, 24)) +>any2 : Symbol(any2, Decl(unparenthesizedConstructorTypeInUnionOrIntersection.ts, 14, 33)) + +type I6 = +>I6 : Symbol(I6, Decl(unparenthesizedConstructorTypeInUnionOrIntersection.ts, 14, 58)) + + & string + & new (foo: any) => void; +>foo : Symbol(foo, Decl(unparenthesizedConstructorTypeInUnionOrIntersection.ts, 17, 9)) + +type M1 = string | number & string | new () => number; +>M1 : Symbol(M1, Decl(unparenthesizedConstructorTypeInUnionOrIntersection.ts, 17, 27)) + +type M2 = any & string | any & new () => void; +>M2 : Symbol(M2, Decl(unparenthesizedConstructorTypeInUnionOrIntersection.ts, 19, 54)) + +type M3 = any & new (foo: any) => void | new () => void & any; +>M3 : Symbol(M3, Decl(unparenthesizedConstructorTypeInUnionOrIntersection.ts, 20, 46)) +>foo : Symbol(foo, Decl(unparenthesizedConstructorTypeInUnionOrIntersection.ts, 21, 21)) + +type OK1 = string | (new ()=> void); +>OK1 : Symbol(OK1, Decl(unparenthesizedConstructorTypeInUnionOrIntersection.ts, 21, 62)) + +type OK2 = string | (new ()=> string | number); +>OK2 : Symbol(OK2, Decl(unparenthesizedConstructorTypeInUnionOrIntersection.ts, 23, 36)) + diff --git a/tests/baselines/reference/unparenthesizedConstructorTypeInUnionOrIntersection.types b/tests/baselines/reference/unparenthesizedConstructorTypeInUnionOrIntersection.types new file mode 100644 index 0000000000000..bc133b3fe8218 --- /dev/null +++ b/tests/baselines/reference/unparenthesizedConstructorTypeInUnionOrIntersection.types @@ -0,0 +1,71 @@ +=== tests/cases/compiler/unparenthesizedConstructorTypeInUnionOrIntersection.ts === +type U1 = string | new () => void; +>U1 : U1 + +type U2 = string | new (foo: number) => void +>U2 : U2 +>foo : number + +type U3 = | new () => number +>U3 : new () => number + +type U4 = | new (foo?: number) => void; +>U4 : new (foo?: number) => void +>foo : number + +type U5 = string | new (number: number, foo?: string) => void | number; +>U5 : U5 +>number : number +>foo : string + +type U6 = +>U6 : U6 + + | string + | new (...args: any[]) => void +>args : any[] + + | number; + +type I1 = string & new () => void; +>I1 : I1 + +type I2 = string & new (...foo: number[]) => void; +>I2 : I2 +>foo : number[] + +type I3 = & new () => boolean +>I3 : new () => boolean + +type I4 = & new () => boolean & null; +>I4 : new () => boolean & null +>null : null + +type I5 = string & new (any: any, any2: any) => any & any; +>I5 : I5 +>any : any +>any2 : any + +type I6 = +>I6 : I6 + + & string + & new (foo: any) => void; +>foo : any + +type M1 = string | number & string | new () => number; +>M1 : M1 + +type M2 = any & string | any & new () => void; +>M2 : any + +type M3 = any & new (foo: any) => void | new () => void & any; +>M3 : any +>foo : any + +type OK1 = string | (new ()=> void); +>OK1 : OK1 + +type OK2 = string | (new ()=> string | number); +>OK2 : OK2 + diff --git a/tests/cases/compiler/unparenthesizedConstructorTypeInUnionOrIntersection.ts b/tests/cases/compiler/unparenthesizedConstructorTypeInUnionOrIntersection.ts new file mode 100644 index 0000000000000..0c24b47804fa8 --- /dev/null +++ b/tests/cases/compiler/unparenthesizedConstructorTypeInUnionOrIntersection.ts @@ -0,0 +1,25 @@ +type U1 = string | new () => void; +type U2 = string | new (foo: number) => void +type U3 = | new () => number +type U4 = | new (foo?: number) => void; +type U5 = string | new (number: number, foo?: string) => void | number; +type U6 = + | string + | new (...args: any[]) => void + | number; + +type I1 = string & new () => void; +type I2 = string & new (...foo: number[]) => void; +type I3 = & new () => boolean +type I4 = & new () => boolean & null; +type I5 = string & new (any: any, any2: any) => any & any; +type I6 = + & string + & new (foo: any) => void; + +type M1 = string | number & string | new () => number; +type M2 = any & string | any & new () => void; +type M3 = any & new (foo: any) => void | new () => void & any; + +type OK1 = string | (new ()=> void); +type OK2 = string | (new ()=> string | number); From e1c2a5148e10889700bfd6e7bd31811c24d83ad7 Mon Sep 17 00:00:00 2001 From: uhyo Date: Tue, 14 Jul 2020 11:30:35 +0900 Subject: [PATCH 04/10] Update src/compiler/parser.ts Co-authored-by: Daniel Rosenwasser --- src/compiler/parser.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 26628f1ae0d1d..33644cdd0ebdf 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -3573,8 +3573,9 @@ namespace ts { functionTypeDiagnostic: DiagnosticMessage, constructorTypeDiagnostic: DiagnosticMessage ): TypeNode | undefined { - // function type notations or constructor type notations are not allowed in this context, - // but we try parsing them for graceful error message + // the function type and constructor type shorthand notation + // are not allowed directly in unions and intersections, but we'll + // try to parse them gracefully and issue a helpful message. if (isStartOfFunctionType() || token() === SyntaxKind.NewKeyword) { const type = parseFunctionOrConstructorType(); parseErrorAtRange(type, isFunctionTypeNode(type) ? functionTypeDiagnostic : constructorTypeDiagnostic); From 4193b788b03522e1c9421545bb90787db56fffc1 Mon Sep 17 00:00:00 2001 From: uhyo Date: Tue, 14 Jul 2020 11:39:11 +0900 Subject: [PATCH 05/10] pass isInUnionType to parseFunctionOrConstructorTypeToError --- src/compiler/parser.ts | 46 ++++++++++++++++++++++-------------------- 1 file changed, 24 insertions(+), 22 deletions(-) diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 33644cdd0ebdf..1d27d65adbb08 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -3570,15 +3570,31 @@ namespace ts { } function parseFunctionOrConstructorTypeToError( - functionTypeDiagnostic: DiagnosticMessage, - constructorTypeDiagnostic: DiagnosticMessage + isInUnionType: boolean ): TypeNode | undefined { // the function type and constructor type shorthand notation // are not allowed directly in unions and intersections, but we'll // try to parse them gracefully and issue a helpful message. if (isStartOfFunctionType() || token() === SyntaxKind.NewKeyword) { const type = parseFunctionOrConstructorType(); - parseErrorAtRange(type, isFunctionTypeNode(type) ? functionTypeDiagnostic : constructorTypeDiagnostic); + let diagnostic: DiagnosticMessage; + if (isFunctionTypeNode(type)) { + if (isInUnionType) { + diagnostic = Diagnostics.Function_type_notation_must_be_parenthesized_when_used_in_a_union_type; + } + else { + diagnostic = Diagnostics.Function_type_notation_must_be_parenthesized_when_used_in_an_intersection_type; + } + } + else { + if (isInUnionType) { + diagnostic = Diagnostics.Constructor_type_notation_must_be_parenthesized_when_used_in_a_union_type; + } + else { + diagnostic = Diagnostics.Constructor_type_notation_must_be_parenthesized_when_used_in_an_intersection_type; + } + } + parseErrorAtRange(type, diagnostic); return type; } return undefined; @@ -3587,19 +3603,17 @@ namespace ts { function parseUnionOrIntersectionType( operator: SyntaxKind.BarToken | SyntaxKind.AmpersandToken, parseConstituentType: () => TypeNode, - createTypeNode: (types: NodeArray) => UnionOrIntersectionTypeNode, - functionTypeDiagnostic: DiagnosticMessage, - constructorTypeDiagnostic: DiagnosticMessage + createTypeNode: (types: NodeArray) => UnionOrIntersectionTypeNode ): TypeNode { const pos = getNodePos(); const hasLeadingOperator = parseOptional(operator); let type = hasLeadingOperator ? - parseFunctionOrConstructorTypeToError(functionTypeDiagnostic, constructorTypeDiagnostic) || parseConstituentType() : + parseFunctionOrConstructorTypeToError(operator === SyntaxKind.BarToken) || parseConstituentType() : parseConstituentType(); if (token() === operator || hasLeadingOperator) { const types = [type]; while (parseOptional(operator)) { - types.push(parseFunctionOrConstructorTypeToError(functionTypeDiagnostic, constructorTypeDiagnostic) || parseConstituentType()); + types.push(parseFunctionOrConstructorTypeToError(operator === SyntaxKind.BarToken) || parseConstituentType()); } type = finishNode(createTypeNode(createNodeArray(types, pos)), pos); } @@ -3607,23 +3621,11 @@ namespace ts { } function parseIntersectionTypeOrHigher(): TypeNode { - return parseUnionOrIntersectionType( - SyntaxKind.AmpersandToken, - parseTypeOperatorOrHigher, - factory.createIntersectionTypeNode, - Diagnostics.Function_type_notation_must_be_parenthesized_when_used_in_an_intersection_type, - Diagnostics.Constructor_type_notation_must_be_parenthesized_when_used_in_an_intersection_type - ); + return parseUnionOrIntersectionType(SyntaxKind.AmpersandToken, parseTypeOperatorOrHigher, factory.createIntersectionTypeNode); } function parseUnionTypeOrHigher(): TypeNode { - return parseUnionOrIntersectionType( - SyntaxKind.BarToken, - parseIntersectionTypeOrHigher, - factory.createUnionTypeNode, - Diagnostics.Function_type_notation_must_be_parenthesized_when_used_in_a_union_type, - Diagnostics.Constructor_type_notation_must_be_parenthesized_when_used_in_a_union_type - ); + return parseUnionOrIntersectionType(SyntaxKind.BarToken, parseIntersectionTypeOrHigher, factory.createUnionTypeNode); } function isStartOfFunctionType(): boolean { From 6bda0cc131f883793aff762a7af341280485e1f6 Mon Sep 17 00:00:00 2001 From: uhyo Date: Wed, 15 Jul 2020 10:08:49 +0900 Subject: [PATCH 06/10] Apply suggestions from code review Co-authored-by: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com> --- src/compiler/parser.ts | 24 +++++++++--------------- 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 1d27d65adbb08..6fadc3d301909 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -3579,20 +3579,15 @@ namespace ts { const type = parseFunctionOrConstructorType(); let diagnostic: DiagnosticMessage; if (isFunctionTypeNode(type)) { - if (isInUnionType) { - diagnostic = Diagnostics.Function_type_notation_must_be_parenthesized_when_used_in_a_union_type; - } - else { - diagnostic = Diagnostics.Function_type_notation_must_be_parenthesized_when_used_in_an_intersection_type; - } + diagnostic = isInUnionType + ? Diagnostics.Function_type_notation_must_be_parenthesized_when_used_in_a_union_type; + : Diagnostics.Function_type_notation_must_be_parenthesized_when_used_in_an_intersection_type; } else { - if (isInUnionType) { - diagnostic = Diagnostics.Constructor_type_notation_must_be_parenthesized_when_used_in_a_union_type; - } - else { - diagnostic = Diagnostics.Constructor_type_notation_must_be_parenthesized_when_used_in_an_intersection_type; - } + diagnostic = isInUnionType ? + ? Diagnostics.Constructor_type_notation_must_be_parenthesized_when_used_in_a_union_type + : Diagnostics.Constructor_type_notation_must_be_parenthesized_when_used_in_an_intersection_type; + } parseErrorAtRange(type, diagnostic); return type; @@ -3607,9 +3602,8 @@ namespace ts { ): TypeNode { const pos = getNodePos(); const hasLeadingOperator = parseOptional(operator); - let type = hasLeadingOperator ? - parseFunctionOrConstructorTypeToError(operator === SyntaxKind.BarToken) || parseConstituentType() : - parseConstituentType(); + let type = hasLeadingOperator && parseFunctionOrConstructorTypeToError(operator === SyntaxKind.BarToken) + || parseConstituentType(); if (token() === operator || hasLeadingOperator) { const types = [type]; while (parseOptional(operator)) { From 38167bc897c8f8dc08b97df5ffdd37f3589cba27 Mon Sep 17 00:00:00 2001 From: uhyo Date: Wed, 15 Jul 2020 10:15:29 +0900 Subject: [PATCH 07/10] syntax fix --- src/compiler/parser.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 6fadc3d301909..a787a7d8358b3 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -3580,11 +3580,11 @@ namespace ts { let diagnostic: DiagnosticMessage; if (isFunctionTypeNode(type)) { diagnostic = isInUnionType - ? Diagnostics.Function_type_notation_must_be_parenthesized_when_used_in_a_union_type; + ? Diagnostics.Function_type_notation_must_be_parenthesized_when_used_in_a_union_type : Diagnostics.Function_type_notation_must_be_parenthesized_when_used_in_an_intersection_type; } else { - diagnostic = isInUnionType ? + diagnostic = isInUnionType ? Diagnostics.Constructor_type_notation_must_be_parenthesized_when_used_in_a_union_type : Diagnostics.Constructor_type_notation_must_be_parenthesized_when_used_in_an_intersection_type; @@ -3602,7 +3602,7 @@ namespace ts { ): TypeNode { const pos = getNodePos(); const hasLeadingOperator = parseOptional(operator); - let type = hasLeadingOperator && parseFunctionOrConstructorTypeToError(operator === SyntaxKind.BarToken) + let type = hasLeadingOperator && parseFunctionOrConstructorTypeToError(operator === SyntaxKind.BarToken) || parseConstituentType(); if (token() === operator || hasLeadingOperator) { const types = [type]; From 32562ff094ee7075614735d260163dc934d2b3d1 Mon Sep 17 00:00:00 2001 From: uhyo Date: Wed, 15 Jul 2020 10:15:52 +0900 Subject: [PATCH 08/10] refactor isStartOfFunctionType into isStartOfFunctionTypeOrConstructorType --- src/compiler/parser.ts | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index a787a7d8358b3..3aa55a67859a8 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -3575,7 +3575,7 @@ namespace ts { // the function type and constructor type shorthand notation // are not allowed directly in unions and intersections, but we'll // try to parse them gracefully and issue a helpful message. - if (isStartOfFunctionType() || token() === SyntaxKind.NewKeyword) { + if (isStartOfFunctionTypeOrConstructorType()) { const type = parseFunctionOrConstructorType(); let diagnostic: DiagnosticMessage; if (isFunctionTypeNode(type)) { @@ -3622,11 +3622,14 @@ namespace ts { return parseUnionOrIntersectionType(SyntaxKind.BarToken, parseIntersectionTypeOrHigher, factory.createUnionTypeNode); } - function isStartOfFunctionType(): boolean { + function isStartOfFunctionTypeOrConstructorType(): boolean { if (token() === SyntaxKind.LessThanToken) { return true; } - return token() === SyntaxKind.OpenParenToken && lookAhead(isUnambiguouslyStartOfFunctionType); + if(token() === SyntaxKind.OpenParenToken && lookAhead(isUnambiguouslyStartOfFunctionType)) { + return true; + } + return token() === SyntaxKind.NewKeyword; } function skipParameterStart(): boolean { @@ -3711,7 +3714,7 @@ namespace ts { } function parseTypeWorker(noConditionalTypes?: boolean): TypeNode { - if (isStartOfFunctionType() || token() === SyntaxKind.NewKeyword) { + if (isStartOfFunctionTypeOrConstructorType()) { return parseFunctionOrConstructorType(); } const pos = getNodePos(); From 98b9d82a8d40e94cfe0dedd7cc1d1ad451427400 Mon Sep 17 00:00:00 2001 From: uhyo Date: Wed, 15 Jul 2020 15:16:31 +0900 Subject: [PATCH 09/10] Update src/compiler/parser.ts Co-authored-by: Daniel Rosenwasser --- src/compiler/parser.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 3aa55a67859a8..b691345011bfb 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -3626,7 +3626,7 @@ namespace ts { if (token() === SyntaxKind.LessThanToken) { return true; } - if(token() === SyntaxKind.OpenParenToken && lookAhead(isUnambiguouslyStartOfFunctionType)) { + if (token() === SyntaxKind.OpenParenToken && lookAhead(isUnambiguouslyStartOfFunctionType)) { return true; } return token() === SyntaxKind.NewKeyword; From e4c84fcd0e89ed05994652056d79e7241d9c205e Mon Sep 17 00:00:00 2001 From: uhyo Date: Wed, 15 Jul 2020 15:19:06 +0900 Subject: [PATCH 10/10] hoist isUnionType --- src/compiler/parser.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index b691345011bfb..bf353c8e0ff13 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -3601,13 +3601,14 @@ namespace ts { createTypeNode: (types: NodeArray) => UnionOrIntersectionTypeNode ): TypeNode { const pos = getNodePos(); + const isUnionType = operator === SyntaxKind.BarToken; const hasLeadingOperator = parseOptional(operator); - let type = hasLeadingOperator && parseFunctionOrConstructorTypeToError(operator === SyntaxKind.BarToken) + let type = hasLeadingOperator && parseFunctionOrConstructorTypeToError(isUnionType) || parseConstituentType(); if (token() === operator || hasLeadingOperator) { const types = [type]; while (parseOptional(operator)) { - types.push(parseFunctionOrConstructorTypeToError(operator === SyntaxKind.BarToken) || parseConstituentType()); + types.push(parseFunctionOrConstructorTypeToError(isUnionType) || parseConstituentType()); } type = finishNode(createTypeNode(createNodeArray(types, pos)), pos); }