mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-06 02:33:53 -06:00
JSDoc:positional matching of destructured params (#23307)
* JSDoc:positional matching of destructured params 1. When looking up tags for a parameter whose name is a binding pattern, use the index of the parameter to get the type. 2. When reporting errors for `@param` tags with no matching parameter name, do not report the error for tags whose index in the `@param` tag list matches the index of a parameter whose name is a binding pattern. * Change to an assert * Improve comment text
This commit is contained in:
parent
4b706fc479
commit
22919d57fe
@ -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);
|
||||
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,
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
82
tests/baselines/reference/jsdocParamTag2.errors.txt
Normal file
82
tests/baselines/reference/jsdocParamTag2.errors.txt
Normal file
@ -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
tests/baselines/reference/jsdocParamTag2.symbols
Normal file
117
tests/baselines/reference/jsdocParamTag2.symbols
Normal file
@ -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
tests/baselines/reference/jsdocParamTag2.types
Normal file
117
tests/baselines/reference/jsdocParamTag2.types
Normal file
@ -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
|
||||
|
||||
71
tests/cases/conformance/jsdoc/jsdocParamTag2.ts
Normal file
71
tests/cases/conformance/jsdoc/jsdocParamTag2.ts
Normal file
@ -0,0 +1,71 @@
|
||||
// @allowJS: true
|
||||
// @checkJS: true
|
||||
// @noEmit: true
|
||||
// @Filename: 0.js
|
||||
|
||||
// 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
|
||||
* @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}) {}
|
||||
/**
|
||||
* @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}) {}
|
||||
Loading…
x
Reference in New Issue
Block a user