Skip to content

Commit 0fa838a

Browse files
authored
Brackets and postfix= in @param add undefined (#22514)
* Brackets and postfix= in `@param` add undefined Previously they only added optionality. Note that, unlike Typescript, when a parameter initializer is specified in jsdoc, it does not remove undefined in the *body* of the function. That's because TS will generate initialisation code, but JS won't, so the author will have to manually write code to remove undefined from the type. ```js /** @param {number} [a=101] */ function f(a) { // a: number | undefined here if (!a) { a = 101 } // a: number here } ``` Note that we don't check that 1. the initializer value is actually assigned to the parameter. 2. the initializer's type matches the declared type of the parameter. Pretty much we just parse it and leave it alone. * Address PR comments
1 parent 23a64fe commit 0fa838a

File tree

5 files changed

+133
-16
lines changed

5 files changed

+133
-16
lines changed

src/compiler/checker.ts

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4167,7 +4167,17 @@ namespace ts {
41674167
return getTypeForBindingElement(<BindingElement>declaration);
41684168
}
41694169

4170-
const isOptional = !isBindingElement(declaration) && !isVariableDeclaration(declaration) && !!declaration.questionToken && includeOptionality;
4170+
let isOptional = false;
4171+
if (includeOptionality) {
4172+
if (isInJavaScriptFile(declaration) && isParameter(declaration)) {
4173+
const parameterTags = getJSDocParameterTags(declaration);
4174+
isOptional = !!(parameterTags && parameterTags.length > 0 && find(parameterTags, tag => tag.isBracketed));
4175+
}
4176+
if (!isBindingElement(declaration) && !isVariableDeclaration(declaration) && !!declaration.questionToken) {
4177+
isOptional = true;
4178+
}
4179+
}
4180+
41714181
// Use type from type annotation if one is present
41724182
const declaredType = tryGetTypeFromEffectiveTypeNode(declaration);
41734183
if (declaredType) {
@@ -8670,9 +8680,10 @@ namespace ts {
86708680
return getTypeFromIntersectionTypeNode(<IntersectionTypeNode>node);
86718681
case SyntaxKind.JSDocNullableType:
86728682
return getTypeFromJSDocNullableTypeNode(<JSDocNullableType>node);
8683+
case SyntaxKind.JSDocOptionalType:
8684+
return addOptionality(getTypeFromTypeNode((node as JSDocOptionalType).type));
86738685
case SyntaxKind.ParenthesizedType:
86748686
case SyntaxKind.JSDocNonNullableType:
8675-
case SyntaxKind.JSDocOptionalType:
86768687
case SyntaxKind.JSDocTypeExpression:
86778688
return getTypeFromTypeNode((<ParenthesizedTypeNode | JSDocTypeReferencingNode | JSDocTypeExpression>node).type);
86788689
case SyntaxKind.JSDocVariadicType:

tests/baselines/reference/jsdocParamTagTypeLiteral.types

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -23,18 +23,18 @@ normal(12);
2323
* @param {string} [opts1.w="hi"] doc5
2424
*/
2525
function foo1(opts1) {
26-
>foo1 : (opts1: { x: string; y?: string; z?: string; w?: string; }) => void
27-
>opts1 : { x: string; y?: string; z?: string; w?: string; }
26+
>foo1 : (opts1: { x: string; y?: string | undefined; z?: string; w?: string; }) => void
27+
>opts1 : { x: string; y?: string | undefined; z?: string; w?: string; }
2828

2929
opts1.x;
3030
>opts1.x : string
31-
>opts1 : { x: string; y?: string; z?: string; w?: string; }
31+
>opts1 : { x: string; y?: string | undefined; z?: string; w?: string; }
3232
>x : string
3333
}
3434

3535
foo1({x: 'abc'});
3636
>foo1({x: 'abc'}) : void
37-
>foo1 : (opts1: { x: string; y?: string; z?: string; w?: string; }) => void
37+
>foo1 : (opts1: { x: string; y?: string | undefined; z?: string; w?: string; }) => void
3838
>{x: 'abc'} : { x: string; }
3939
>x : string
4040
>'abc' : "abc"
@@ -45,20 +45,20 @@ foo1({x: 'abc'});
4545
* @param {string=} opts2[].anotherY
4646
*/
4747
function foo2(/** @param opts2 bad idea theatre! */opts2) {
48-
>foo2 : (opts2: { anotherX: string; anotherY?: string; }[]) => void
49-
>opts2 : { anotherX: string; anotherY?: string; }[]
48+
>foo2 : (opts2: { anotherX: string; anotherY?: string | undefined; }[]) => void
49+
>opts2 : { anotherX: string; anotherY?: string | undefined; }[]
5050

5151
opts2[0].anotherX;
5252
>opts2[0].anotherX : string
53-
>opts2[0] : { anotherX: string; anotherY?: string; }
54-
>opts2 : { anotherX: string; anotherY?: string; }[]
53+
>opts2[0] : { anotherX: string; anotherY?: string | undefined; }
54+
>opts2 : { anotherX: string; anotherY?: string | undefined; }[]
5555
>0 : 0
5656
>anotherX : string
5757
}
5858

5959
foo2([{anotherX: "world"}]);
6060
>foo2([{anotherX: "world"}]) : void
61-
>foo2 : (opts2: { anotherX: string; anotherY?: string; }[]) => void
61+
>foo2 : (opts2: { anotherX: string; anotherY?: string | undefined; }[]) => void
6262
>[{anotherX: "world"}] : { anotherX: string; }[]
6363
>{anotherX: "world"} : { anotherX: string; }
6464
>anotherX : string
@@ -92,20 +92,20 @@ foo3({x: 'abc'});
9292
* @param {string} [opts4[].w="hi"]
9393
*/
9494
function foo4(opts4) {
95-
>foo4 : (opts4: { x: string; y?: string; z?: string; w?: string; }[]) => void
96-
>opts4 : { x: string; y?: string; z?: string; w?: string; }[]
95+
>foo4 : (opts4: { x: string; y?: string | undefined; z?: string; w?: string; }[]) => void
96+
>opts4 : { x: string; y?: string | undefined; z?: string; w?: string; }[]
9797

9898
opts4[0].x;
9999
>opts4[0].x : string
100-
>opts4[0] : { x: string; y?: string; z?: string; w?: string; }
101-
>opts4 : { x: string; y?: string; z?: string; w?: string; }[]
100+
>opts4[0] : { x: string; y?: string | undefined; z?: string; w?: string; }
101+
>opts4 : { x: string; y?: string | undefined; z?: string; w?: string; }[]
102102
>0 : 0
103103
>x : string
104104
}
105105

106106
foo4([{ x: 'hi' }]);
107107
>foo4([{ x: 'hi' }]) : void
108-
>foo4 : (opts4: { x: string; y?: string; z?: string; w?: string; }[]) => void
108+
>foo4 : (opts4: { x: string; y?: string | undefined; z?: string; w?: string; }[]) => void
109109
>[{ x: 'hi' }] : { x: string; }[]
110110
>{ x: 'hi' } : { x: string; }
111111
>x : string
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
=== tests/cases/conformance/jsdoc/a.js ===
2+
/**
3+
* @param {number} [p]
4+
* @param {number=} q
5+
* @param {number} [r=101]
6+
*/
7+
function f(p, q, r) {
8+
>f : Symbol(f, Decl(a.js, 0, 0))
9+
>p : Symbol(p, Decl(a.js, 5, 11))
10+
>q : Symbol(q, Decl(a.js, 5, 13))
11+
>r : Symbol(r, Decl(a.js, 5, 16))
12+
13+
p = undefined
14+
>p : Symbol(p, Decl(a.js, 5, 11))
15+
>undefined : Symbol(undefined)
16+
17+
q = undefined
18+
>q : Symbol(q, Decl(a.js, 5, 13))
19+
>undefined : Symbol(undefined)
20+
21+
// note that, unlike TS, JSDOC [r=101] retains | undefined because
22+
// there's no code emitted to get rid of it.
23+
r = undefined
24+
>r : Symbol(r, Decl(a.js, 5, 16))
25+
>undefined : Symbol(undefined)
26+
}
27+
f()
28+
>f : Symbol(f, Decl(a.js, 0, 0))
29+
30+
f(undefined, undefined, undefined)
31+
>f : Symbol(f, Decl(a.js, 0, 0))
32+
>undefined : Symbol(undefined)
33+
>undefined : Symbol(undefined)
34+
>undefined : Symbol(undefined)
35+
36+
f(1, 2, 3)
37+
>f : Symbol(f, Decl(a.js, 0, 0))
38+
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
=== tests/cases/conformance/jsdoc/a.js ===
2+
/**
3+
* @param {number} [p]
4+
* @param {number=} q
5+
* @param {number} [r=101]
6+
*/
7+
function f(p, q, r) {
8+
>f : (p?: number | undefined, q?: number | undefined, r?: number | undefined) => void
9+
>p : number | undefined
10+
>q : number | undefined
11+
>r : number | undefined
12+
13+
p = undefined
14+
>p = undefined : undefined
15+
>p : number | undefined
16+
>undefined : undefined
17+
18+
q = undefined
19+
>q = undefined : undefined
20+
>q : number | undefined
21+
>undefined : undefined
22+
23+
// note that, unlike TS, JSDOC [r=101] retains | undefined because
24+
// there's no code emitted to get rid of it.
25+
r = undefined
26+
>r = undefined : undefined
27+
>r : number | undefined
28+
>undefined : undefined
29+
}
30+
f()
31+
>f() : void
32+
>f : (p?: number | undefined, q?: number | undefined, r?: number | undefined) => void
33+
34+
f(undefined, undefined, undefined)
35+
>f(undefined, undefined, undefined) : void
36+
>f : (p?: number | undefined, q?: number | undefined, r?: number | undefined) => void
37+
>undefined : undefined
38+
>undefined : undefined
39+
>undefined : undefined
40+
41+
f(1, 2, 3)
42+
>f(1, 2, 3) : void
43+
>f : (p?: number | undefined, q?: number | undefined, r?: number | undefined) => void
44+
>1 : 1
45+
>2 : 2
46+
>3 : 3
47+
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// @noEmit: true
2+
// @allowJs: true
3+
// @checkJs: true
4+
// @strict: true
5+
// @Filename: a.js
6+
7+
/**
8+
* @param {number} [p]
9+
* @param {number=} q
10+
* @param {number} [r=101]
11+
*/
12+
function f(p, q, r) {
13+
p = undefined
14+
q = undefined
15+
// note that, unlike TS, JSDOC [r=101] retains | undefined because
16+
// there's no code emitted to get rid of it.
17+
r = undefined
18+
}
19+
f()
20+
f(undefined, undefined, undefined)
21+
f(1, 2, 3)

0 commit comments

Comments
 (0)