Skip to content

Commit c3c6be6

Browse files
authored
In JSDoc, parse postfix-? below conditional types/tuple types (microsoft#39123)
Outside of JSDoc comments, postfix-? is parsed at lower precedence than the `?` of conditional types, and a postfix-? inside a tuple type results in the type being marked optional. This PR changes JSDoc parsing to behave the same way, which means that 1. Conditional types are allowed in JSDoc. Fixes microsoft#37166. 2. Tuple types' postfix-? syntax is interpreted correctly in JSDoc. Fixes microsoft#38747. The breaking change is that a postfix-? type followed by another postfix type, like `[]` or `!`, is parsed as a conditional type. [Postfix-? is not common](microsoft#37166 (comment)), so this is an acceptable breaking change. A postfix-? type `T?` is still parsed everywhere else and treated as `T | null`.
1 parent d7aa5f3 commit c3c6be6

File tree

5 files changed

+75
-39
lines changed

5 files changed

+75
-39
lines changed

src/compiler/parser.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -3115,7 +3115,7 @@ namespace ts {
31153115
return finishNode(factory.createRestTypeNode(parseType()), pos);
31163116
}
31173117
const type = parseType();
3118-
if (!(contextFlags & NodeFlags.JSDoc) && isJSDocNullableType(type) && type.pos === type.type.pos) {
3118+
if (isJSDocNullableType(type) && type.pos === type.type.pos) {
31193119
const node = factory.createOptionalTypeNode(type.type);
31203120
setTextRange(node, type);
31213121
(node as Mutable<Node>).flags = type.flags;
@@ -3356,7 +3356,7 @@ namespace ts {
33563356
break;
33573357
case SyntaxKind.QuestionToken:
33583358
// If not in JSDoc and next token is start of a type we have a conditional type
3359-
if (!(contextFlags & NodeFlags.JSDoc) && lookAhead(nextTokenIsStartOfType)) {
3359+
if (lookAhead(nextTokenIsStartOfType)) {
33603360
return type;
33613361
}
33623362
nextToken();
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,29 @@
1+
tests/cases/conformance/jsdoc/prefixPostfix.js(5,18): error TS8024: JSDoc '@param' tag has name '', but there is no parameter with that name.
2+
tests/cases/conformance/jsdoc/prefixPostfix.js(5,18): error TS1005: '}' expected.
13
tests/cases/conformance/jsdoc/prefixPostfix.js(8,12): error TS1014: A rest parameter must be last in a parameter list.
24
tests/cases/conformance/jsdoc/prefixPostfix.js(9,12): error TS1014: A rest parameter must be last in a parameter list.
35
tests/cases/conformance/jsdoc/prefixPostfix.js(10,12): error TS1014: A rest parameter must be last in a parameter list.
4-
tests/cases/conformance/jsdoc/prefixPostfix.js(11,12): error TS1014: A rest parameter must be last in a parameter list.
6+
tests/cases/conformance/jsdoc/prefixPostfix.js(11,21): error TS8024: JSDoc '@param' tag has name '', but there is no parameter with that name.
7+
tests/cases/conformance/jsdoc/prefixPostfix.js(11,21): error TS1005: '}' expected.
58
tests/cases/conformance/jsdoc/prefixPostfix.js(12,12): error TS1014: A rest parameter must be last in a parameter list.
69
tests/cases/conformance/jsdoc/prefixPostfix.js(13,12): error TS1014: A rest parameter must be last in a parameter list.
10+
tests/cases/conformance/jsdoc/prefixPostfix.js(14,21): error TS8024: JSDoc '@param' tag has name '', but there is no parameter with that name.
11+
tests/cases/conformance/jsdoc/prefixPostfix.js(14,21): error TS1005: '}' expected.
12+
tests/cases/conformance/jsdoc/prefixPostfix.js(18,21): error TS7006: Parameter 'a' implicitly has an 'any' type.
13+
tests/cases/conformance/jsdoc/prefixPostfix.js(18,39): error TS7006: Parameter 'h' implicitly has an 'any' type.
14+
tests/cases/conformance/jsdoc/prefixPostfix.js(18,48): error TS7006: Parameter 'k' implicitly has an 'any' type.
715

816

9-
==== tests/cases/conformance/jsdoc/prefixPostfix.js (6 errors) ====
17+
==== tests/cases/conformance/jsdoc/prefixPostfix.js (14 errors) ====
1018
/**
1119
* @param {number![]} x - number[]
1220
* @param {!number[]} y - number[]
1321
* @param {(number[])!} z - number[]
14-
* @param {number?[]} a - (number | null)[]
22+
* @param {number?[]} a - parse error without parentheses
23+
24+
!!! error TS8024: JSDoc '@param' tag has name '', but there is no parameter with that name.
25+
~
26+
!!! error TS1005: '}' expected.
1527
* @param {?number[]} b - number[] | null
1628
* @param {(number[])?} c - number[] | null
1729
* @param {...?number} e - (number | null)[]
@@ -23,17 +35,31 @@ tests/cases/conformance/jsdoc/prefixPostfix.js(13,12): error TS1014: A rest para
2335
* @param {...number!?} g - number[] | null
2436
~~~~~~~~~~~
2537
!!! error TS1014: A rest parameter must be last in a parameter list.
26-
* @param {...number?!} h - number[] | null
27-
~~~~~~~~~~~
28-
!!! error TS1014: A rest parameter must be last in a parameter list.
38+
* @param {...number?!} h - parse error without parentheses (also nonsensical)
39+
40+
!!! error TS8024: JSDoc '@param' tag has name '', but there is no parameter with that name.
41+
~
42+
!!! error TS1005: '}' expected.
2943
* @param {...number[]} i - number[][]
3044
~~~~~~~~~~~
3145
!!! error TS1014: A rest parameter must be last in a parameter list.
3246
* @param {...number![]?} j - number[][] | null
3347
~~~~~~~~~~~~~
3448
!!! error TS1014: A rest parameter must be last in a parameter list.
35-
* @param {...number?[]!} k - (number[] | null)[]
49+
* @param {...number?[]!} k - parse error without parentheses
50+
51+
!!! error TS8024: JSDoc '@param' tag has name '', but there is no parameter with that name.
52+
~
53+
!!! error TS1005: '}' expected.
54+
* @param {number extends number ? true : false} l - conditional types work
55+
* @param {[number, number?]} m - [number, (number | undefined)?]
3656
*/
37-
function f(x, y, z, a, b, c, e, f, g, h, i, j, k) {
57+
function f(x, y, z, a, b, c, e, f, g, h, i, j, k, l, m) {
58+
~
59+
!!! error TS7006: Parameter 'a' implicitly has an 'any' type.
60+
~
61+
!!! error TS7006: Parameter 'h' implicitly has an 'any' type.
62+
~
63+
!!! error TS7006: Parameter 'k' implicitly has an 'any' type.
3864
}
3965

tests/baselines/reference/jsdocPrefixPostfixParsing.symbols

+21-17
Original file line numberDiff line numberDiff line change
@@ -3,31 +3,35 @@
33
* @param {number![]} x - number[]
44
* @param {!number[]} y - number[]
55
* @param {(number[])!} z - number[]
6-
* @param {number?[]} a - (number | null)[]
6+
* @param {number?[]} a - parse error without parentheses
77
* @param {?number[]} b - number[] | null
88
* @param {(number[])?} c - number[] | null
99
* @param {...?number} e - (number | null)[]
1010
* @param {...number?} f - number[] | null
1111
* @param {...number!?} g - number[] | null
12-
* @param {...number?!} h - number[] | null
12+
* @param {...number?!} h - parse error without parentheses (also nonsensical)
1313
* @param {...number[]} i - number[][]
1414
* @param {...number![]?} j - number[][] | null
15-
* @param {...number?[]!} k - (number[] | null)[]
15+
* @param {...number?[]!} k - parse error without parentheses
16+
* @param {number extends number ? true : false} l - conditional types work
17+
* @param {[number, number?]} m - [number, (number | undefined)?]
1618
*/
17-
function f(x, y, z, a, b, c, e, f, g, h, i, j, k) {
19+
function f(x, y, z, a, b, c, e, f, g, h, i, j, k, l, m) {
1820
>f : Symbol(f, Decl(prefixPostfix.js, 0, 0))
19-
>x : Symbol(x, Decl(prefixPostfix.js, 15, 11))
20-
>y : Symbol(y, Decl(prefixPostfix.js, 15, 13))
21-
>z : Symbol(z, Decl(prefixPostfix.js, 15, 16))
22-
>a : Symbol(a, Decl(prefixPostfix.js, 15, 19))
23-
>b : Symbol(b, Decl(prefixPostfix.js, 15, 22))
24-
>c : Symbol(c, Decl(prefixPostfix.js, 15, 25))
25-
>e : Symbol(e, Decl(prefixPostfix.js, 15, 28))
26-
>f : Symbol(f, Decl(prefixPostfix.js, 15, 31))
27-
>g : Symbol(g, Decl(prefixPostfix.js, 15, 34))
28-
>h : Symbol(h, Decl(prefixPostfix.js, 15, 37))
29-
>i : Symbol(i, Decl(prefixPostfix.js, 15, 40))
30-
>j : Symbol(j, Decl(prefixPostfix.js, 15, 43))
31-
>k : Symbol(k, Decl(prefixPostfix.js, 15, 46))
21+
>x : Symbol(x, Decl(prefixPostfix.js, 17, 11))
22+
>y : Symbol(y, Decl(prefixPostfix.js, 17, 13))
23+
>z : Symbol(z, Decl(prefixPostfix.js, 17, 16))
24+
>a : Symbol(a, Decl(prefixPostfix.js, 17, 19))
25+
>b : Symbol(b, Decl(prefixPostfix.js, 17, 22))
26+
>c : Symbol(c, Decl(prefixPostfix.js, 17, 25))
27+
>e : Symbol(e, Decl(prefixPostfix.js, 17, 28))
28+
>f : Symbol(f, Decl(prefixPostfix.js, 17, 31))
29+
>g : Symbol(g, Decl(prefixPostfix.js, 17, 34))
30+
>h : Symbol(h, Decl(prefixPostfix.js, 17, 37))
31+
>i : Symbol(i, Decl(prefixPostfix.js, 17, 40))
32+
>j : Symbol(j, Decl(prefixPostfix.js, 17, 43))
33+
>k : Symbol(k, Decl(prefixPostfix.js, 17, 46))
34+
>l : Symbol(l, Decl(prefixPostfix.js, 17, 49))
35+
>m : Symbol(m, Decl(prefixPostfix.js, 17, 52))
3236
}
3337

tests/baselines/reference/jsdocPrefixPostfixParsing.types

+12-8
Original file line numberDiff line numberDiff line change
@@ -3,31 +3,35 @@
33
* @param {number![]} x - number[]
44
* @param {!number[]} y - number[]
55
* @param {(number[])!} z - number[]
6-
* @param {number?[]} a - (number | null)[]
6+
* @param {number?[]} a - parse error without parentheses
77
* @param {?number[]} b - number[] | null
88
* @param {(number[])?} c - number[] | null
99
* @param {...?number} e - (number | null)[]
1010
* @param {...number?} f - number[] | null
1111
* @param {...number!?} g - number[] | null
12-
* @param {...number?!} h - number[] | null
12+
* @param {...number?!} h - parse error without parentheses (also nonsensical)
1313
* @param {...number[]} i - number[][]
1414
* @param {...number![]?} j - number[][] | null
15-
* @param {...number?[]!} k - (number[] | null)[]
15+
* @param {...number?[]!} k - parse error without parentheses
16+
* @param {number extends number ? true : false} l - conditional types work
17+
* @param {[number, number?]} m - [number, (number | undefined)?]
1618
*/
17-
function f(x, y, z, a, b, c, e, f, g, h, i, j, k) {
18-
>f : (x: number[], y: number[], z: (number[]), a: (number | null)[], b: number[] | null, c: (number[]) | null, e: (number | null)[], f: (number | null)[], g: (number | null)[], h: (number | null)[], i: number[][], j: (number[] | null)[], k: (number | null)[][]) => void
19+
function f(x, y, z, a, b, c, e, f, g, h, i, j, k, l, m) {
20+
>f : (x: number[], y: number[], z: (number[]), a: any, b: number[] | null, c: (number[]) | null, e: (number | null)[], f: (number | null)[], g: (number | null)[], h: any, i: number[][], j: (number[] | null)[], k: any, l: number extends number ? true : false, m: [number, number?]) => void
1921
>x : number[]
2022
>y : number[]
2123
>z : number[]
22-
>a : (number | null)[]
24+
>a : any
2325
>b : number[] | null
2426
>c : number[] | null
2527
>e : number | null | undefined
2628
>f : number | null | undefined
2729
>g : number | null | undefined
28-
>h : number | null | undefined
30+
>h : any
2931
>i : number[] | undefined
3032
>j : number[] | null | undefined
31-
>k : (number | null)[] | undefined
33+
>k : any
34+
>l : true
35+
>m : [number, (number | undefined)?]
3236
}
3337

tests/cases/conformance/jsdoc/jsdocPrefixPostfixParsing.ts

+6-4
Original file line numberDiff line numberDiff line change
@@ -10,16 +10,18 @@
1010
* @param {number![]} x - number[]
1111
* @param {!number[]} y - number[]
1212
* @param {(number[])!} z - number[]
13-
* @param {number?[]} a - (number | null)[]
13+
* @param {number?[]} a - parse error without parentheses
1414
* @param {?number[]} b - number[] | null
1515
* @param {(number[])?} c - number[] | null
1616
* @param {...?number} e - (number | null)[]
1717
* @param {...number?} f - number[] | null
1818
* @param {...number!?} g - number[] | null
19-
* @param {...number?!} h - number[] | null
19+
* @param {...number?!} h - parse error without parentheses (also nonsensical)
2020
* @param {...number[]} i - number[][]
2121
* @param {...number![]?} j - number[][] | null
22-
* @param {...number?[]!} k - (number[] | null)[]
22+
* @param {...number?[]!} k - parse error without parentheses
23+
* @param {number extends number ? true : false} l - conditional types work
24+
* @param {[number, number?]} m - [number, (number | undefined)?]
2325
*/
24-
function f(x, y, z, a, b, c, e, f, g, h, i, j, k) {
26+
function f(x, y, z, a, b, c, e, f, g, h, i, j, k, l, m) {
2527
}

0 commit comments

Comments
 (0)