diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index a79ed0c5428b5..c38e4f67fe287 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -3124,15 +3124,16 @@ namespace ts { } function parseUnionOrIntersectionType(kind: SyntaxKind.UnionType | SyntaxKind.IntersectionType, parseConstituentType: () => TypeNode, operator: SyntaxKind.BarToken | SyntaxKind.AmpersandToken): TypeNode { - parseOptional(operator); + const start = scanner.getStartPos(); + const hasLeadingOperator = parseOptional(operator); let type = parseConstituentType(); - if (token() === operator) { + if (token() === operator || hasLeadingOperator) { const types = [type]; while (parseOptional(operator)) { types.push(parseConstituentType()); } - const node = createNode(kind, type.pos); - node.types = createNodeArray(types, type.pos); + const node = createNode(kind, start); + node.types = createNodeArray(types, start); type = finishNode(node); } return type; diff --git a/src/testRunner/unittests/jsDocParsing.ts b/src/testRunner/unittests/jsDocParsing.ts index 4171a330f3228..941b1557450a9 100644 --- a/src/testRunner/unittests/jsDocParsing.ts +++ b/src/testRunner/unittests/jsDocParsing.ts @@ -37,6 +37,8 @@ namespace ts { parsesCorrectly("callSignatureInRecordType", "{{(): number}}"); parsesCorrectly("methodInRecordType", "{{foo(): number}}"); parsesCorrectly("unionType", "{(number|string)}"); + parsesCorrectly("unionTypeWithLeadingOperator", "{( | number | string )}"); + parsesCorrectly("unionTypeWithOneElementAndLeadingOperator", "{( | number )}"); parsesCorrectly("topLevelNoParenUnionType", "{number|string}"); parsesCorrectly("functionType1", "{function()}"); parsesCorrectly("functionType2", "{function(string, boolean)}"); diff --git a/tests/baselines/reference/JSDocParsing/TypeExpressions.parsesCorrectly.unionTypeWithLeadingOperator.json b/tests/baselines/reference/JSDocParsing/TypeExpressions.parsesCorrectly.unionTypeWithLeadingOperator.json new file mode 100644 index 0000000000000..fd53183d6168f --- /dev/null +++ b/tests/baselines/reference/JSDocParsing/TypeExpressions.parsesCorrectly.unionTypeWithLeadingOperator.json @@ -0,0 +1,37 @@ +{ + "kind": "ParenthesizedType", + "pos": 1, + "end": 22, + "flags": "JSDoc", + "modifierFlagsCache": 0, + "transformFlags": 0, + "type": { + "kind": "UnionType", + "pos": 2, + "end": 20, + "flags": "JSDoc", + "modifierFlagsCache": 0, + "transformFlags": 0, + "types": { + "0": { + "kind": "NumberKeyword", + "pos": 4, + "end": 11, + "flags": "JSDoc", + "modifierFlagsCache": 0, + "transformFlags": 0 + }, + "1": { + "kind": "StringKeyword", + "pos": 13, + "end": 20, + "flags": "JSDoc", + "modifierFlagsCache": 0, + "transformFlags": 0 + }, + "length": 2, + "pos": 2, + "end": 20 + } + } +} \ No newline at end of file diff --git a/tests/baselines/reference/JSDocParsing/TypeExpressions.parsesCorrectly.unionTypeWithOneElementAndLeadingOperator.json b/tests/baselines/reference/JSDocParsing/TypeExpressions.parsesCorrectly.unionTypeWithOneElementAndLeadingOperator.json new file mode 100644 index 0000000000000..7fd1b1b98f2b5 --- /dev/null +++ b/tests/baselines/reference/JSDocParsing/TypeExpressions.parsesCorrectly.unionTypeWithOneElementAndLeadingOperator.json @@ -0,0 +1,29 @@ +{ + "kind": "ParenthesizedType", + "pos": 1, + "end": 13, + "flags": "JSDoc", + "modifierFlagsCache": 0, + "transformFlags": 0, + "type": { + "kind": "UnionType", + "pos": 2, + "end": 11, + "flags": "JSDoc", + "modifierFlagsCache": 0, + "transformFlags": 0, + "types": { + "0": { + "kind": "NumberKeyword", + "pos": 4, + "end": 11, + "flags": "JSDoc", + "modifierFlagsCache": 0, + "transformFlags": 0 + }, + "length": 1, + "pos": 2, + "end": 11 + } + } +} \ No newline at end of file diff --git a/tests/baselines/reference/unionTypeWithLeadingOperator.js b/tests/baselines/reference/unionTypeWithLeadingOperator.js index 0fb366d538e7b..84572d9430e66 100644 --- a/tests/baselines/reference/unionTypeWithLeadingOperator.js +++ b/tests/baselines/reference/unionTypeWithLeadingOperator.js @@ -5,6 +5,16 @@ type B = | { type: "DECREMENT" }; type C = [| 0 | 1, | "foo" | "bar"]; - + +export type D = + /*leading0*/ + | /*leading1*/ 1 /*trailing1*/ + | /*leading2*/ 2 /*trailing2*/; //// [unionTypeWithLeadingOperator.js] +"use strict"; +exports.__esModule = true; + + +//// [unionTypeWithLeadingOperator.d.ts] +export declare type D = /*leading1*/ 1 | /*leading2*/ 2; diff --git a/tests/baselines/reference/unionTypeWithLeadingOperator.symbols b/tests/baselines/reference/unionTypeWithLeadingOperator.symbols index 8c15d299c4864..4ded4561a6228 100644 --- a/tests/baselines/reference/unionTypeWithLeadingOperator.symbols +++ b/tests/baselines/reference/unionTypeWithLeadingOperator.symbols @@ -14,3 +14,9 @@ type B = type C = [| 0 | 1, | "foo" | "bar"]; >C : Symbol(C, Decl(unionTypeWithLeadingOperator.ts, 3, 26)) +export type D = +>D : Symbol(D, Decl(unionTypeWithLeadingOperator.ts, 5, 36)) + + /*leading0*/ + | /*leading1*/ 1 /*trailing1*/ + | /*leading2*/ 2 /*trailing2*/; diff --git a/tests/baselines/reference/unionTypeWithLeadingOperator.types b/tests/baselines/reference/unionTypeWithLeadingOperator.types index 2ca15fb54a298..930ff71f3f74b 100644 --- a/tests/baselines/reference/unionTypeWithLeadingOperator.types +++ b/tests/baselines/reference/unionTypeWithLeadingOperator.types @@ -14,3 +14,9 @@ type B = type C = [| 0 | 1, | "foo" | "bar"]; >C : [0 | 1, "foo" | "bar"] +export type D = +>D : D + + /*leading0*/ + | /*leading1*/ 1 /*trailing1*/ + | /*leading2*/ 2 /*trailing2*/; diff --git a/tests/cases/compiler/unionTypeWithLeadingOperator.ts b/tests/cases/compiler/unionTypeWithLeadingOperator.ts index 9f6d272ce7c03..17d9b051ef77a 100644 --- a/tests/cases/compiler/unionTypeWithLeadingOperator.ts +++ b/tests/cases/compiler/unionTypeWithLeadingOperator.ts @@ -1,6 +1,12 @@ +// @declaration: true type A = | string; type B = | { type: "INCREMENT" } | { type: "DECREMENT" }; type C = [| 0 | 1, | "foo" | "bar"]; + +export type D = + /*leading0*/ + | /*leading1*/ 1 /*trailing1*/ + | /*leading2*/ 2 /*trailing2*/; \ No newline at end of file