diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 4140f49f9a6f4..e2a9bac10a9b8 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -1833,6 +1833,8 @@ namespace ts { // type parameters are visible in parameter list, return type and type parameter list ? lastLocation === (location as FunctionLikeDeclaration).type || lastLocation.kind === SyntaxKind.Parameter || + lastLocation.kind === SyntaxKind.JSDocParameterTag || + lastLocation.kind === SyntaxKind.JSDocReturnTag || lastLocation.kind === SyntaxKind.TypeParameter // local types not visible outside the function body : false; @@ -2101,8 +2103,8 @@ namespace ts { lastSelfReferenceLocation = location; } lastLocation = location; - location = isJSDocTemplateTag(location) ? - getEffectiveContainerForJSDocTemplateTag(location) || location.parent : + location = isJSDocTemplateTag(location) ? getEffectiveContainerForJSDocTemplateTag(location) || location.parent : + isJSDocParameterTag(location) || isJSDocReturnTag(location) ? getHostSignatureFromJSDoc(location) || location.parent : location.parent; } @@ -3374,16 +3376,20 @@ namespace ts { return; } const host = getJSDocHost(node); - if (host && - isExpressionStatement(host) && - isBinaryExpression(host.expression) && - getAssignmentDeclarationKind(host.expression) === AssignmentDeclarationKind.PrototypeProperty) { - // X.prototype.m = /** @param {K} p */ function () { } <-- look for K on X's declaration + if (host && isExpressionStatement(host) && isPrototypePropertyAssignment(host.expression)) { + // /** @param {K} p */ X.prototype.m = function () { } <-- look for K on X's declaration const symbol = getSymbolOfNode(host.expression.left); if (symbol) { return getDeclarationOfJSPrototypeContainer(symbol); } } + if (host && isFunctionExpression(host) && isPrototypePropertyAssignment(host.parent) && isExpressionStatement(host.parent.parent)) { + // X.prototype.m = /** @param {K} p */ function () { } <-- look for K on X's declaration + const symbol = getSymbolOfNode(host.parent.left); + if (symbol) { + return getDeclarationOfJSPrototypeContainer(symbol); + } + } if (host && (isObjectLiteralMethod(host) || isPropertyAssignment(host)) && isBinaryExpression(host.parent.parent) && getAssignmentDeclarationKind(host.parent.parent) === AssignmentDeclarationKind.Prototype) { diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index ab7d0e6375ea5..b652a3c0d6fae 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -2506,7 +2506,7 @@ namespace ts { return expr.right; } - export function isPrototypePropertyAssignment(node: Node): boolean { + export function isPrototypePropertyAssignment(node: Node): node is BinaryExpression { return isBinaryExpression(node) && getAssignmentDeclarationKind(node) === AssignmentDeclarationKind.PrototypeProperty; } diff --git a/tests/baselines/reference/constructorFunctionMethodTypeParameters.symbols b/tests/baselines/reference/constructorFunctionMethodTypeParameters.symbols new file mode 100644 index 0000000000000..7af38bcb4cf29 --- /dev/null +++ b/tests/baselines/reference/constructorFunctionMethodTypeParameters.symbols @@ -0,0 +1,73 @@ +=== tests/cases/conformance/salsa/constructorFunctionMethodTypeParameters.js === +/** + * @template {string} T + * @param {T} t + */ +function Cls(t) { +>Cls : Symbol(Cls, Decl(constructorFunctionMethodTypeParameters.js, 0, 0)) +>t : Symbol(t, Decl(constructorFunctionMethodTypeParameters.js, 4, 13)) + + this.t = t; +>this.t : Symbol(Cls.t, Decl(constructorFunctionMethodTypeParameters.js, 4, 17)) +>this : Symbol(Cls, Decl(constructorFunctionMethodTypeParameters.js, 0, 0)) +>t : Symbol(Cls.t, Decl(constructorFunctionMethodTypeParameters.js, 4, 17)) +>t : Symbol(t, Decl(constructorFunctionMethodTypeParameters.js, 4, 13)) +} + +/** + * @template {string} V + * @param {T} t + * @param {V} v + * @return {V} + */ +Cls.prototype.topLevelComment = function (t, v) { +>Cls.prototype : Symbol(Cls.topLevelComment, Decl(constructorFunctionMethodTypeParameters.js, 6, 1)) +>Cls : Symbol(Cls, Decl(constructorFunctionMethodTypeParameters.js, 0, 0)) +>prototype : Symbol(Function.prototype, Decl(lib.es5.d.ts, --, --)) +>topLevelComment : Symbol(Cls.topLevelComment, Decl(constructorFunctionMethodTypeParameters.js, 6, 1)) +>t : Symbol(t, Decl(constructorFunctionMethodTypeParameters.js, 14, 42)) +>v : Symbol(v, Decl(constructorFunctionMethodTypeParameters.js, 14, 44)) + + return v +>v : Symbol(v, Decl(constructorFunctionMethodTypeParameters.js, 14, 44)) + +}; + +Cls.prototype.nestedComment = +>Cls.prototype : Symbol(Cls.nestedComment, Decl(constructorFunctionMethodTypeParameters.js, 16, 2)) +>Cls : Symbol(Cls, Decl(constructorFunctionMethodTypeParameters.js, 0, 0)) +>prototype : Symbol(Function.prototype, Decl(lib.es5.d.ts, --, --)) +>nestedComment : Symbol(Cls.nestedComment, Decl(constructorFunctionMethodTypeParameters.js, 16, 2)) + + /** + * @template {string} U + * @param {T} t + * @param {U} u + * @return {T} + */ + function (t, u) { +>t : Symbol(t, Decl(constructorFunctionMethodTypeParameters.js, 25, 14)) +>u : Symbol(u, Decl(constructorFunctionMethodTypeParameters.js, 25, 16)) + + return t +>t : Symbol(t, Decl(constructorFunctionMethodTypeParameters.js, 25, 14)) + + }; + +var c = new Cls('a'); +>c : Symbol(c, Decl(constructorFunctionMethodTypeParameters.js, 29, 3)) +>Cls : Symbol(Cls, Decl(constructorFunctionMethodTypeParameters.js, 0, 0)) + +const s = c.topLevelComment('a', 'b'); +>s : Symbol(s, Decl(constructorFunctionMethodTypeParameters.js, 30, 5)) +>c.topLevelComment : Symbol(Cls.topLevelComment, Decl(constructorFunctionMethodTypeParameters.js, 6, 1)) +>c : Symbol(c, Decl(constructorFunctionMethodTypeParameters.js, 29, 3)) +>topLevelComment : Symbol(Cls.topLevelComment, Decl(constructorFunctionMethodTypeParameters.js, 6, 1)) + +const t = c.nestedComment('a', 'b'); +>t : Symbol(t, Decl(constructorFunctionMethodTypeParameters.js, 31, 5)) +>c.nestedComment : Symbol(Cls.nestedComment, Decl(constructorFunctionMethodTypeParameters.js, 16, 2)) +>c : Symbol(c, Decl(constructorFunctionMethodTypeParameters.js, 29, 3)) +>nestedComment : Symbol(Cls.nestedComment, Decl(constructorFunctionMethodTypeParameters.js, 16, 2)) + + diff --git a/tests/baselines/reference/constructorFunctionMethodTypeParameters.types b/tests/baselines/reference/constructorFunctionMethodTypeParameters.types new file mode 100644 index 0000000000000..707a8922c1ac0 --- /dev/null +++ b/tests/baselines/reference/constructorFunctionMethodTypeParameters.types @@ -0,0 +1,88 @@ +=== tests/cases/conformance/salsa/constructorFunctionMethodTypeParameters.js === +/** + * @template {string} T + * @param {T} t + */ +function Cls(t) { +>Cls : typeof Cls +>t : T + + this.t = t; +>this.t = t : T +>this.t : any +>this : this +>t : any +>t : T +} + +/** + * @template {string} V + * @param {T} t + * @param {V} v + * @return {V} + */ +Cls.prototype.topLevelComment = function (t, v) { +>Cls.prototype.topLevelComment = function (t, v) { return v} : (t: T, v: V) => V +>Cls.prototype.topLevelComment : any +>Cls.prototype : any +>Cls : typeof Cls +>prototype : any +>topLevelComment : any +>function (t, v) { return v} : (t: T, v: V) => V +>t : T +>v : V + + return v +>v : V + +}; + +Cls.prototype.nestedComment = +>Cls.prototype.nestedComment = /** * @template {string} U * @param {T} t * @param {U} u * @return {T} */ function (t, u) { return t } : (t: T, u: U) => T +>Cls.prototype.nestedComment : any +>Cls.prototype : any +>Cls : typeof Cls +>prototype : any +>nestedComment : any + + /** + * @template {string} U + * @param {T} t + * @param {U} u + * @return {T} + */ + function (t, u) { +>function (t, u) { return t } : (t: T, u: U) => T +>t : T +>u : U + + return t +>t : T + + }; + +var c = new Cls('a'); +>c : Cls<"a"> +>new Cls('a') : Cls<"a"> +>Cls : typeof Cls +>'a' : "a" + +const s = c.topLevelComment('a', 'b'); +>s : "b" +>c.topLevelComment('a', 'b') : "b" +>c.topLevelComment : (t: "a", v: V) => V +>c : Cls<"a"> +>topLevelComment : (t: "a", v: V) => V +>'a' : "a" +>'b' : "b" + +const t = c.nestedComment('a', 'b'); +>t : "a" +>c.nestedComment('a', 'b') : "a" +>c.nestedComment : (t: "a", u: U) => "a" +>c : Cls<"a"> +>nestedComment : (t: "a", u: U) => "a" +>'a' : "a" +>'b' : "b" + + diff --git a/tests/cases/conformance/salsa/constructorFunctionMethodTypeParameters.ts b/tests/cases/conformance/salsa/constructorFunctionMethodTypeParameters.ts new file mode 100644 index 0000000000000..bbe6810a15090 --- /dev/null +++ b/tests/cases/conformance/salsa/constructorFunctionMethodTypeParameters.ts @@ -0,0 +1,37 @@ +// @noEmit: true +// @allowJs: true +// @checkJs: true +// @filename: constructorFunctionMethodTypeParameters.js +/** + * @template {string} T + * @param {T} t + */ +function Cls(t) { + this.t = t; +} + +/** + * @template {string} V + * @param {T} t + * @param {V} v + * @return {V} + */ +Cls.prototype.topLevelComment = function (t, v) { + return v +}; + +Cls.prototype.nestedComment = + /** + * @template {string} U + * @param {T} t + * @param {U} u + * @return {T} + */ + function (t, u) { + return t + }; + +var c = new Cls('a'); +const s = c.topLevelComment('a', 'b'); +const t = c.nestedComment('a', 'b'); +