Skip to content

JSDoc:positional matching of destructured params #23307

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Apr 10, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21871,6 +21871,10 @@ namespace ts {
// and give a better error message when the host function mentions `arguments`
// but the tag doesn't have an array type
if (decl) {
const i = getJSDocTags(decl).filter(isJSDocParameterTag).indexOf(node);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe getParameterSymbolFromJSDoc should work for binding patterns too?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's no symbol on a binding pattern, though, so without adding something in the binder there's no benefit.

if (i > -1 && i < decl.parameters.length && isBindingPattern(decl.parameters[i].name)) {
return;
}
if (!containsArgumentsReference(decl)) {
error(node.name,
Diagnostics.JSDoc_param_tag_has_name_0_but_there_is_no_parameter_with_that_name,
Expand Down
18 changes: 14 additions & 4 deletions src/compiler/utilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4632,11 +4632,21 @@ namespace ts {
* parameters by name and binding patterns do not have a name.
*/
export function getJSDocParameterTags(param: ParameterDeclaration): ReadonlyArray<JSDocParameterTag> {
if (param.name && isIdentifier(param.name)) {
const name = param.name.escapedText;
return getJSDocTags(param.parent).filter((tag): tag is JSDocParameterTag => isJSDocParameterTag(tag) && isIdentifier(tag.name) && tag.name.escapedText === name);
if (param.name) {
if (isIdentifier(param.name)) {
const name = param.name.escapedText;
return getJSDocTags(param.parent).filter((tag): tag is JSDocParameterTag => isJSDocParameterTag(tag) && isIdentifier(tag.name) && tag.name.escapedText === name);
}
else {
const i = param.parent.parameters.indexOf(param);
Debug.assert(i > -1, "Parameters should always be in their parents' parameter list");
const paramTags = getJSDocTags(param.parent).filter(isJSDocParameterTag);
if (i < paramTags.length) {
return [paramTags[i]];
}
}
}
// a binding pattern doesn't have a name, so it's not possible to match it a JSDoc parameter, which is identified by name
// return empty array for: out-of-order binding patterns and JSDoc function syntax, which has un-named parameters
return emptyArray;
}

Expand Down
82 changes: 82 additions & 0 deletions tests/baselines/reference/jsdocParamTag2.errors.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
tests/cases/conformance/jsdoc/0.js(56,20): error TS8024: JSDoc '@param' tag has name 'obj', but there is no parameter with that name.
tests/cases/conformance/jsdoc/0.js(61,19): error TS2459: Type 'string' has no property 'a' and no string index signature.
tests/cases/conformance/jsdoc/0.js(61,22): error TS2459: Type 'string' has no property 'b' and no string index signature.
tests/cases/conformance/jsdoc/0.js(63,20): error TS8024: JSDoc '@param' tag has name 'y', but there is no parameter with that name.


==== tests/cases/conformance/jsdoc/0.js (4 errors) ====
// Object literal syntax
/**
* @param {{a: string, b: string}} obj
* @param {string} x
*/
function good1({a, b}, x) {}
/**
* @param {{a: string, b: string}} obj
* @param {{c: number, d: number}} OBJECTION
*/
function good2({a, b}, {c, d}) {}
/**
* @param {number} x
* @param {{a: string, b: string}} obj
* @param {string} y
*/
function good3(x, {a, b}, y) {}
/**
* @param {{a: string, b: string}} obj
*/
function good4({a, b}) {}

// nested object syntax
/**
* @param {Object} obj
* @param {string} obj.a - this is like the saddest way to specify a type
* @param {string} obj.b - but it sure does allow a lot of documentation
* @param {string} x
*/
function good5({a, b}, x) {}
/**
* @param {Object} obj
* @param {string} obj.a
* @param {string} obj.b - but it sure does allow a lot of documentation
* @param {Object} OBJECTION - documentation here too
* @param {string} OBJECTION.c
* @param {string} OBJECTION.d - meh
*/
function good6({a, b}, {c, d}) {}
/**
* @param {number} x
* @param {Object} obj
* @param {string} obj.a
* @param {string} obj.b
* @param {string} y
*/
function good7(x, {a, b}, y) {}
/**
* @param {Object} obj
* @param {string} obj.a
* @param {string} obj.b
*/
function good8({a, b}) {}

/**
* @param {object} obj - this type gets ignored
~~~
!!! error TS8024: JSDoc '@param' tag has name 'obj', but there is no parameter with that name.
* @param {string} obj.a
* @param {string} obj.b - and x's type gets used for both parameters
* @param {string} x
*/
function bad1(x, {a, b}) {}
~
!!! error TS2459: Type 'string' has no property 'a' and no string index signature.
~
!!! error TS2459: Type 'string' has no property 'b' and no string index signature.
/**
* @param {string} y - here, y's type gets ignored but obj's is fine
~
!!! error TS8024: JSDoc '@param' tag has name 'y', but there is no parameter with that name.
* @param {{a: string, b: string}} obj
*/
function bad2(x, {a, b}) {}

117 changes: 117 additions & 0 deletions tests/baselines/reference/jsdocParamTag2.symbols
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
=== tests/cases/conformance/jsdoc/0.js ===
// Object literal syntax
/**
* @param {{a: string, b: string}} obj
* @param {string} x
*/
function good1({a, b}, x) {}
>good1 : Symbol(good1, Decl(0.js, 0, 0))
>a : Symbol(a, Decl(0.js, 5, 16))
>b : Symbol(b, Decl(0.js, 5, 18))
>x : Symbol(x, Decl(0.js, 5, 22))

/**
* @param {{a: string, b: string}} obj
* @param {{c: number, d: number}} OBJECTION
*/
function good2({a, b}, {c, d}) {}
>good2 : Symbol(good2, Decl(0.js, 5, 28))
>a : Symbol(a, Decl(0.js, 10, 16))
>b : Symbol(b, Decl(0.js, 10, 18))
>c : Symbol(c, Decl(0.js, 10, 24))
>d : Symbol(d, Decl(0.js, 10, 26))

/**
* @param {number} x
* @param {{a: string, b: string}} obj
* @param {string} y
*/
function good3(x, {a, b}, y) {}
>good3 : Symbol(good3, Decl(0.js, 10, 33))
>x : Symbol(x, Decl(0.js, 16, 15))
>a : Symbol(a, Decl(0.js, 16, 19))
>b : Symbol(b, Decl(0.js, 16, 21))
>y : Symbol(y, Decl(0.js, 16, 25))

/**
* @param {{a: string, b: string}} obj
*/
function good4({a, b}) {}
>good4 : Symbol(good4, Decl(0.js, 16, 31))
>a : Symbol(a, Decl(0.js, 20, 16))
>b : Symbol(b, Decl(0.js, 20, 18))

// nested object syntax
/**
* @param {Object} obj
* @param {string} obj.a - this is like the saddest way to specify a type
* @param {string} obj.b - but it sure does allow a lot of documentation
* @param {string} x
*/
function good5({a, b}, x) {}
>good5 : Symbol(good5, Decl(0.js, 20, 25))
>a : Symbol(a, Decl(0.js, 29, 16))
>b : Symbol(b, Decl(0.js, 29, 18))
>x : Symbol(x, Decl(0.js, 29, 22))

/**
* @param {Object} obj
* @param {string} obj.a
* @param {string} obj.b - but it sure does allow a lot of documentation
* @param {Object} OBJECTION - documentation here too
* @param {string} OBJECTION.c
* @param {string} OBJECTION.d - meh
*/
function good6({a, b}, {c, d}) {}
>good6 : Symbol(good6, Decl(0.js, 29, 28))
>a : Symbol(a, Decl(0.js, 38, 16))
>b : Symbol(b, Decl(0.js, 38, 18))
>c : Symbol(c, Decl(0.js, 38, 24))
>d : Symbol(d, Decl(0.js, 38, 26))

/**
* @param {number} x
* @param {Object} obj
* @param {string} obj.a
* @param {string} obj.b
* @param {string} y
*/
function good7(x, {a, b}, y) {}
>good7 : Symbol(good7, Decl(0.js, 38, 33))
>x : Symbol(x, Decl(0.js, 46, 15))
>a : Symbol(a, Decl(0.js, 46, 19))
>b : Symbol(b, Decl(0.js, 46, 21))
>y : Symbol(y, Decl(0.js, 46, 25))

/**
* @param {Object} obj
* @param {string} obj.a
* @param {string} obj.b
*/
function good8({a, b}) {}
>good8 : Symbol(good8, Decl(0.js, 46, 31))
>a : Symbol(a, Decl(0.js, 52, 16))
>b : Symbol(b, Decl(0.js, 52, 18))

/**
* @param {object} obj - this type gets ignored
* @param {string} obj.a
* @param {string} obj.b - and x's type gets used for both parameters
* @param {string} x
*/
function bad1(x, {a, b}) {}
>bad1 : Symbol(bad1, Decl(0.js, 52, 25))
>x : Symbol(x, Decl(0.js, 60, 14))
>a : Symbol(a, Decl(0.js, 60, 18))
>b : Symbol(b, Decl(0.js, 60, 20))

/**
* @param {string} y - here, y's type gets ignored but obj's is fine
* @param {{a: string, b: string}} obj
*/
function bad2(x, {a, b}) {}
>bad2 : Symbol(bad2, Decl(0.js, 60, 27))
>x : Symbol(x, Decl(0.js, 65, 14))
>a : Symbol(a, Decl(0.js, 65, 18))
>b : Symbol(b, Decl(0.js, 65, 20))

117 changes: 117 additions & 0 deletions tests/baselines/reference/jsdocParamTag2.types
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
=== tests/cases/conformance/jsdoc/0.js ===
// Object literal syntax
/**
* @param {{a: string, b: string}} obj
* @param {string} x
*/
function good1({a, b}, x) {}
>good1 : ({ a, b }: { a: string; b: string; }, x: string) => void
>a : string
>b : string
>x : string

/**
* @param {{a: string, b: string}} obj
* @param {{c: number, d: number}} OBJECTION
*/
function good2({a, b}, {c, d}) {}
>good2 : ({ a, b }: { a: string; b: string; }, { c, d }: { c: number; d: number; }) => void
>a : string
>b : string
>c : number
>d : number

/**
* @param {number} x
* @param {{a: string, b: string}} obj
* @param {string} y
*/
function good3(x, {a, b}, y) {}
>good3 : (x: number, { a, b }: { a: string; b: string; }, y: string) => void
>x : number
>a : string
>b : string
>y : string

/**
* @param {{a: string, b: string}} obj
*/
function good4({a, b}) {}
>good4 : ({ a, b }: { a: string; b: string; }) => void
>a : string
>b : string

// nested object syntax
/**
* @param {Object} obj
* @param {string} obj.a - this is like the saddest way to specify a type
* @param {string} obj.b - but it sure does allow a lot of documentation
* @param {string} x
*/
function good5({a, b}, x) {}
>good5 : ({ a, b }: { a: string; b: string; }, x: string) => void
>a : string
>b : string
>x : string

/**
* @param {Object} obj
* @param {string} obj.a
* @param {string} obj.b - but it sure does allow a lot of documentation
* @param {Object} OBJECTION - documentation here too
* @param {string} OBJECTION.c
* @param {string} OBJECTION.d - meh
*/
function good6({a, b}, {c, d}) {}
>good6 : ({ a, b }: { a: string; b: string; }, { c, d }: { c: string; d: string; }) => void
>a : string
>b : string
>c : string
>d : string

/**
* @param {number} x
* @param {Object} obj
* @param {string} obj.a
* @param {string} obj.b
* @param {string} y
*/
function good7(x, {a, b}, y) {}
>good7 : (x: number, { a, b }: { a: string; b: string; }, y: string) => void
>x : number
>a : string
>b : string
>y : string

/**
* @param {Object} obj
* @param {string} obj.a
* @param {string} obj.b
*/
function good8({a, b}) {}
>good8 : ({ a, b }: { a: string; b: string; }) => void
>a : string
>b : string

/**
* @param {object} obj - this type gets ignored
* @param {string} obj.a
* @param {string} obj.b - and x's type gets used for both parameters
* @param {string} x
*/
function bad1(x, {a, b}) {}
>bad1 : (x: string, { a, b }: string) => void
>x : string
>a : any
>b : any

/**
* @param {string} y - here, y's type gets ignored but obj's is fine
* @param {{a: string, b: string}} obj
*/
function bad2(x, {a, b}) {}
>bad2 : (x: any, { a, b }: { a: string; b: string; }) => void
>x : any
>a : string
>b : string

Loading