diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index d009a956c43ee..97407ac1fa39f 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -1554,7 +1554,8 @@ namespace ts { function isTypeParameterSymbolDeclaredInContainer(symbol: Symbol, container: Node) { for (const decl of symbol.declarations) { - if (decl.kind === SyntaxKind.TypeParameter && decl.parent === container) { + const parent = isJSDocTemplateTag(decl.parent) ? getJSDocHost(decl.parent) : decl.parent; + if (decl.kind === SyntaxKind.TypeParameter && parent === container) { return true; } } @@ -2060,10 +2061,10 @@ namespace ts { let symbol: Symbol; if (name.kind === SyntaxKind.Identifier) { const message = meaning === namespaceMeaning ? Diagnostics.Cannot_find_namespace_0 : Diagnostics.Cannot_find_name_0; - - symbol = resolveName(location || name, name.escapedText, meaning, ignoreErrors ? undefined : message, name, /*isUse*/ true); + const symbolFromJSPrototype = isInJavaScriptFile(name) && resolveEntityNameFromJSPrototype(name, meaning); + symbol = resolveName(location || name, name.escapedText, meaning, ignoreErrors || symbolFromJSPrototype ? undefined : message, name, /*isUse*/ true); if (!symbol) { - return undefined; + return symbolFromJSPrototype; } } else if (name.kind === SyntaxKind.QualifiedName || name.kind === SyntaxKind.PropertyAccessExpression) { @@ -2114,6 +2115,18 @@ namespace ts { return (symbol.flags & meaning) || dontResolveAlias ? symbol : resolveAlias(symbol); } + function resolveEntityNameFromJSPrototype(name: Identifier, meaning: SymbolFlags) { + if (isJSDocTypeReference(name.parent) && isJSDocTag(name.parent.parent.parent)) { + const host = getJSDocHost(name.parent.parent.parent as JSDocTag); + if (isExpressionStatement(host) && + isBinaryExpression(host.expression) && + getSpecialPropertyAssignmentKind(host.expression) === SpecialPropertyAssignmentKind.PrototypeProperty) { + const secondaryLocation = getSymbolOfNode(host.expression.left).parent.valueDeclaration; + return resolveName(secondaryLocation, name.escapedText, meaning, /*nameNotFoundMessage*/ undefined, name, /*isUse*/ true); + } + } + } + function resolveExternalModuleName(location: Node, moduleReferenceExpression: Expression): Symbol { return resolveExternalModuleNameWorker(location, moduleReferenceExpression, Diagnostics.Cannot_find_module_0); } @@ -4897,8 +4910,7 @@ namespace ts { // in-place and returns the same array. function appendTypeParameters(typeParameters: TypeParameter[], declarations: ReadonlyArray): TypeParameter[] { for (const declaration of declarations) { - const tp = getDeclaredTypeOfTypeParameter(getSymbolOfNode(declaration)); - typeParameters = appendIfUnique(typeParameters, tp); + typeParameters = appendIfUnique(typeParameters, getDeclaredTypeOfTypeParameter(getSymbolOfNode(declaration))); } return typeParameters; } @@ -4958,8 +4970,9 @@ namespace ts { if (node.kind === SyntaxKind.InterfaceDeclaration || node.kind === SyntaxKind.ClassDeclaration || node.kind === SyntaxKind.ClassExpression || node.kind === SyntaxKind.TypeAliasDeclaration) { const declaration = node; - if (declaration.typeParameters) { - result = appendTypeParameters(result, declaration.typeParameters); + const typeParameters = getEffectiveTypeParameterDeclarations(declaration); + if (typeParameters) { + result = appendTypeParameters(result, typeParameters); } } } @@ -5455,9 +5468,10 @@ namespace ts { */ function isThislessFunctionLikeDeclaration(node: FunctionLikeDeclaration): boolean { const returnType = getEffectiveReturnTypeNode(node); + const typeParameters = getEffectiveTypeParameterDeclarations(node); return (node.kind === SyntaxKind.Constructor || (returnType && isThislessType(returnType))) && node.parameters.every(isThislessVariableLikeDeclaration) && - (!node.typeParameters || node.typeParameters.every(isThislessTypeParameter)); + (!typeParameters || typeParameters.every(isThislessTypeParameter)); } /** @@ -6735,8 +6749,7 @@ namespace ts { function getTypeParametersFromDeclaration(declaration: DeclarationWithTypeParameters): TypeParameter[] { let result: TypeParameter[]; forEach(getEffectiveTypeParameterDeclarations(declaration), node => { - const tp = getDeclaredTypeOfTypeParameter(node.symbol); - result = appendIfUnique(result, tp); + result = appendIfUnique(result, getDeclaredTypeOfTypeParameter(node.symbol)); }); return result; } @@ -7547,7 +7560,7 @@ namespace ts { return constraints ? getSubstitutionType(typeVariable, getIntersectionType(append(constraints, typeVariable))) : typeVariable; } - function isJSDocTypeReference(node: NodeWithTypeArguments): node is TypeReferenceNode { + function isJSDocTypeReference(node: Node): node is TypeReferenceNode { return node.flags & NodeFlags.JSDoc && node.kind === SyntaxKind.TypeReference; } @@ -9170,10 +9183,15 @@ namespace ts { // aren't the right hand side of a generic type alias declaration we optimize by reducing the // set of type parameters to those that are possibly referenced in the literal. const declaration = symbol.declarations[0]; - const outerTypeParameters = getOuterTypeParameters(declaration, /*includeThisTypes*/ true) || emptyArray; + let outerTypeParameters = getOuterTypeParameters(declaration, /*includeThisTypes*/ true); + if (isJavaScriptConstructor(declaration)) { + const templateTagParameters = getTypeParametersFromDeclaration(declaration as DeclarationWithTypeParameters); + outerTypeParameters = addRange(outerTypeParameters, templateTagParameters); + } + typeParameters = outerTypeParameters || emptyArray; typeParameters = symbol.flags & SymbolFlags.TypeLiteral && !target.aliasTypeArguments ? - filter(outerTypeParameters, tp => isTypeParameterPossiblyReferenced(tp, declaration)) : - outerTypeParameters; + filter(typeParameters, tp => isTypeParameterPossiblyReferenced(tp, declaration)) : + typeParameters; links.outerTypeParameters = typeParameters; if (typeParameters.length) { links.instantiations = createMap(); @@ -18533,7 +18551,7 @@ namespace ts { } const type = funcSymbol && getJavaScriptClassType(funcSymbol); if (type) { - return type; + return signature.target ? instantiateType(type, signature.mapper) : type; } if (noImplicitAny) { error(node, Diagnostics.new_expression_whose_target_lacks_a_construct_signature_implicitly_has_an_any_type); @@ -22155,8 +22173,9 @@ namespace ts { ): void { // Only report errors on the last declaration for the type parameter container; // this ensures that all uses have been accounted for. - if (!(node.flags & NodeFlags.Ambient) && node.typeParameters && last(getSymbolOfNode(node)!.declarations) === node) { - for (const typeParameter of node.typeParameters) { + const typeParameters = getEffectiveTypeParameterDeclarations(node); + if (!(node.flags & NodeFlags.Ambient) && typeParameters && last(getSymbolOfNode(node)!.declarations) === node) { + for (const typeParameter of typeParameters) { if (!(getMergedSymbol(typeParameter.symbol).isReferenced & SymbolFlags.TypeParameter) && !isIdentifierThatStartsWithUnderScore(typeParameter.name)) { addDiagnostic(UnusedKind.Parameter, createDiagnosticForNode(typeParameter.name, Diagnostics._0_is_declared_but_its_value_is_never_read, symbolName(typeParameter.symbol))); } @@ -23530,20 +23549,21 @@ namespace ts { } } - function areTypeParametersIdentical(declarations: ReadonlyArray, typeParameters: TypeParameter[]) { - const maxTypeArgumentCount = length(typeParameters); - const minTypeArgumentCount = getMinTypeArgumentCount(typeParameters); + function areTypeParametersIdentical(declarations: ReadonlyArray, targetParameters: TypeParameter[]) { + const maxTypeArgumentCount = length(targetParameters); + const minTypeArgumentCount = getMinTypeArgumentCount(targetParameters); for (const declaration of declarations) { // If this declaration has too few or too many type parameters, we report an error - const numTypeParameters = length(declaration.typeParameters); + const sourceParameters = getEffectiveTypeParameterDeclarations(declaration); + const numTypeParameters = length(sourceParameters); if (numTypeParameters < minTypeArgumentCount || numTypeParameters > maxTypeArgumentCount) { return false; } for (let i = 0; i < numTypeParameters; i++) { - const source = declaration.typeParameters[i]; - const target = typeParameters[i]; + const source = sourceParameters[i]; + const target = targetParameters[i]; // If the type parameter node does not have the same as the resolved type // parameter at this position, we report an error. @@ -23604,7 +23624,7 @@ namespace ts { checkCollisionWithRequireExportsInGeneratedCode(node, node.name); checkCollisionWithGlobalPromiseInGeneratedCode(node, node.name); } - checkTypeParameters(node.typeParameters); + checkTypeParameters(getEffectiveTypeParameterDeclarations(node)); checkExportsOnMergedDeclarations(node); const symbol = getSymbolOfNode(node); const type = getDeclaredTypeOfSymbol(symbol); @@ -26836,7 +26856,7 @@ namespace ts { function checkGrammarClassLikeDeclaration(node: ClassLikeDeclaration): boolean { const file = getSourceFileOfNode(node); - return checkGrammarClassDeclarationHeritageClauses(node) || checkGrammarTypeParameterList(node.typeParameters, file); + return checkGrammarClassDeclarationHeritageClauses(node) || checkGrammarTypeParameterList(getEffectiveTypeParameterDeclarations(node), file); } function checkGrammarArrowFunction(node: Node, file: SourceFile): boolean { diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index e9d6f026b4e86..ffde94a518dee 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -3055,11 +3055,11 @@ namespace ts { * Gets the effective type parameters. If the node was parsed in a * JavaScript file, gets the type parameters from the `@template` tag from JSDoc. */ - export function getEffectiveTypeParameterDeclarations(node: DeclarationWithTypeParameters): ReadonlyArray | undefined { + export function getEffectiveTypeParameterDeclarations(node: DeclarationWithTypeParameters) { return node.typeParameters || (isInJavaScriptFile(node) ? getJSDocTypeParameterDeclarations(node) : undefined); } - export function getJSDocTypeParameterDeclarations(node: DeclarationWithTypeParameters): ReadonlyArray { + export function getJSDocTypeParameterDeclarations(node: DeclarationWithTypeParameters) { const templateTag = getJSDocTemplateTag(node); return templateTag && templateTag.typeParameters; } diff --git a/tests/baselines/reference/jsdocTemplateClass.errors.txt b/tests/baselines/reference/jsdocTemplateClass.errors.txt new file mode 100644 index 0000000000000..4598e9f320f6d --- /dev/null +++ b/tests/baselines/reference/jsdocTemplateClass.errors.txt @@ -0,0 +1,31 @@ +tests/cases/conformance/jsdoc/templateTagOnClasses.js(24,1): error TS2322: Type 'boolean' is not assignable to type 'number'. + + +==== tests/cases/conformance/jsdoc/templateTagOnClasses.js (1 errors) ==== + /** + * @template {T} + * @typedef {(t: T) => T} Id + */ + class Foo { + /** @typedef {(t: T) => T} Id2 */ + /** @param {T} x */ + constructor (x) { + this.a = x + } + /** + * + * @param {T} x + * @param {Id} y + * @param {Id2} alpha + * @return {T} + */ + foo(x, y, alpha) { + return alpha(y(x)) + } + } + var f = new Foo(1) + var g = new Foo(false) + f.a = g.a + ~~~ +!!! error TS2322: Type 'boolean' is not assignable to type 'number'. + \ No newline at end of file diff --git a/tests/baselines/reference/jsdocTemplateClass.symbols b/tests/baselines/reference/jsdocTemplateClass.symbols new file mode 100644 index 0000000000000..d80b73bc4ac21 --- /dev/null +++ b/tests/baselines/reference/jsdocTemplateClass.symbols @@ -0,0 +1,54 @@ +=== tests/cases/conformance/jsdoc/templateTagOnClasses.js === +/** + * @template {T} + * @typedef {(t: T) => T} Id + */ +class Foo { +>Foo : Symbol(Foo, Decl(templateTagOnClasses.js, 0, 0)) + + /** @typedef {(t: T) => T} Id2 */ + /** @param {T} x */ + constructor (x) { +>x : Symbol(x, Decl(templateTagOnClasses.js, 7, 17)) + + this.a = x +>this.a : Symbol(Foo.a, Decl(templateTagOnClasses.js, 7, 21)) +>this : Symbol(Foo, Decl(templateTagOnClasses.js, 0, 0)) +>a : Symbol(Foo.a, Decl(templateTagOnClasses.js, 7, 21)) +>x : Symbol(x, Decl(templateTagOnClasses.js, 7, 17)) + } + /** + * + * @param {T} x + * @param {Id} y + * @param {Id2} alpha + * @return {T} + */ + foo(x, y, alpha) { +>foo : Symbol(Foo.foo, Decl(templateTagOnClasses.js, 9, 5)) +>x : Symbol(x, Decl(templateTagOnClasses.js, 17, 8)) +>y : Symbol(y, Decl(templateTagOnClasses.js, 17, 10)) +>alpha : Symbol(alpha, Decl(templateTagOnClasses.js, 17, 13)) + + return alpha(y(x)) +>alpha : Symbol(alpha, Decl(templateTagOnClasses.js, 17, 13)) +>y : Symbol(y, Decl(templateTagOnClasses.js, 17, 10)) +>x : Symbol(x, Decl(templateTagOnClasses.js, 17, 8)) + } +} +var f = new Foo(1) +>f : Symbol(f, Decl(templateTagOnClasses.js, 21, 3)) +>Foo : Symbol(Foo, Decl(templateTagOnClasses.js, 0, 0)) + +var g = new Foo(false) +>g : Symbol(g, Decl(templateTagOnClasses.js, 22, 3)) +>Foo : Symbol(Foo, Decl(templateTagOnClasses.js, 0, 0)) + +f.a = g.a +>f.a : Symbol(Foo.a, Decl(templateTagOnClasses.js, 7, 21)) +>f : Symbol(f, Decl(templateTagOnClasses.js, 21, 3)) +>a : Symbol(Foo.a, Decl(templateTagOnClasses.js, 7, 21)) +>g.a : Symbol(Foo.a, Decl(templateTagOnClasses.js, 7, 21)) +>g : Symbol(g, Decl(templateTagOnClasses.js, 22, 3)) +>a : Symbol(Foo.a, Decl(templateTagOnClasses.js, 7, 21)) + diff --git a/tests/baselines/reference/jsdocTemplateClass.types b/tests/baselines/reference/jsdocTemplateClass.types new file mode 100644 index 0000000000000..aa6a685e34fae --- /dev/null +++ b/tests/baselines/reference/jsdocTemplateClass.types @@ -0,0 +1,62 @@ +=== tests/cases/conformance/jsdoc/templateTagOnClasses.js === +/** + * @template {T} + * @typedef {(t: T) => T} Id + */ +class Foo { +>Foo : Foo + + /** @typedef {(t: T) => T} Id2 */ + /** @param {T} x */ + constructor (x) { +>x : T + + this.a = x +>this.a = x : T +>this.a : T +>this : this +>a : T +>x : T + } + /** + * + * @param {T} x + * @param {Id} y + * @param {Id2} alpha + * @return {T} + */ + foo(x, y, alpha) { +>foo : (x: T, y: (t: T) => T, alpha: (t: T) => T) => T +>x : T +>y : (t: T) => T +>alpha : (t: T) => T + + return alpha(y(x)) +>alpha(y(x)) : T +>alpha : (t: T) => T +>y(x) : T +>y : (t: T) => T +>x : T + } +} +var f = new Foo(1) +>f : Foo +>new Foo(1) : Foo +>Foo : typeof Foo +>1 : 1 + +var g = new Foo(false) +>g : Foo +>new Foo(false) : Foo +>Foo : typeof Foo +>false : false + +f.a = g.a +>f.a = g.a : boolean +>f.a : number +>f : Foo +>a : number +>g.a : boolean +>g : Foo +>a : boolean + diff --git a/tests/baselines/reference/jsdocTemplateConstructorFunction.errors.txt b/tests/baselines/reference/jsdocTemplateConstructorFunction.errors.txt new file mode 100644 index 0000000000000..225d277c4fff8 --- /dev/null +++ b/tests/baselines/reference/jsdocTemplateConstructorFunction.errors.txt @@ -0,0 +1,28 @@ +tests/cases/conformance/jsdoc/templateTagOnConstructorFunctions.js(21,1): error TS2322: Type 'false' is not assignable to type 'number'. + + +==== tests/cases/conformance/jsdoc/templateTagOnConstructorFunctions.js (1 errors) ==== + /** + * @template {T} + * @typedef {(t: T) => T} Id + * @param {T} t + */ + function Zet(t) { + /** @type {T} */ + this.u + this.t = t + } + /** + * @param {T} v + * @param {Id} id + */ + Zet.prototype.add = function(v, id) { + this.u = v || this.t + return id(this.u) + } + var z = new Zet(1) + z.t = 2 + z.u = false + ~~~ +!!! error TS2322: Type 'false' is not assignable to type 'number'. + \ No newline at end of file diff --git a/tests/baselines/reference/jsdocTemplateConstructorFunction.symbols b/tests/baselines/reference/jsdocTemplateConstructorFunction.symbols new file mode 100644 index 0000000000000..8cf2ee99e57ea --- /dev/null +++ b/tests/baselines/reference/jsdocTemplateConstructorFunction.symbols @@ -0,0 +1,57 @@ +=== tests/cases/conformance/jsdoc/templateTagOnConstructorFunctions.js === +/** + * @template {T} + * @typedef {(t: T) => T} Id + * @param {T} t + */ +function Zet(t) { +>Zet : Symbol(Zet, Decl(templateTagOnConstructorFunctions.js, 0, 0)) +>t : Symbol(t, Decl(templateTagOnConstructorFunctions.js, 5, 13)) + + /** @type {T} */ + this.u + this.t = t +>t : Symbol(Zet.t, Decl(templateTagOnConstructorFunctions.js, 7, 10)) +>t : Symbol(t, Decl(templateTagOnConstructorFunctions.js, 5, 13)) +} +/** + * @param {T} v + * @param {Id} id + */ +Zet.prototype.add = function(v, id) { +>Zet.prototype : Symbol(Zet.add, Decl(templateTagOnConstructorFunctions.js, 9, 1)) +>Zet : Symbol(Zet, Decl(templateTagOnConstructorFunctions.js, 0, 0)) +>prototype : Symbol(Function.prototype, Decl(lib.d.ts, --, --)) +>add : Symbol(Zet.add, Decl(templateTagOnConstructorFunctions.js, 9, 1)) +>v : Symbol(v, Decl(templateTagOnConstructorFunctions.js, 14, 29)) +>id : Symbol(id, Decl(templateTagOnConstructorFunctions.js, 14, 31)) + + this.u = v || this.t +>this.u : Symbol(Zet.u, Decl(templateTagOnConstructorFunctions.js, 5, 17), Decl(templateTagOnConstructorFunctions.js, 14, 37)) +>this : Symbol(Zet, Decl(templateTagOnConstructorFunctions.js, 0, 0)) +>u : Symbol(Zet.u, Decl(templateTagOnConstructorFunctions.js, 5, 17), Decl(templateTagOnConstructorFunctions.js, 14, 37)) +>v : Symbol(v, Decl(templateTagOnConstructorFunctions.js, 14, 29)) +>this.t : Symbol(Zet.t, Decl(templateTagOnConstructorFunctions.js, 7, 10)) +>this : Symbol(Zet, Decl(templateTagOnConstructorFunctions.js, 0, 0)) +>t : Symbol(Zet.t, Decl(templateTagOnConstructorFunctions.js, 7, 10)) + + return id(this.u) +>id : Symbol(id, Decl(templateTagOnConstructorFunctions.js, 14, 31)) +>this.u : Symbol(Zet.u, Decl(templateTagOnConstructorFunctions.js, 5, 17), Decl(templateTagOnConstructorFunctions.js, 14, 37)) +>this : Symbol(Zet, Decl(templateTagOnConstructorFunctions.js, 0, 0)) +>u : Symbol(Zet.u, Decl(templateTagOnConstructorFunctions.js, 5, 17), Decl(templateTagOnConstructorFunctions.js, 14, 37)) +} +var z = new Zet(1) +>z : Symbol(z, Decl(templateTagOnConstructorFunctions.js, 18, 3)) +>Zet : Symbol(Zet, Decl(templateTagOnConstructorFunctions.js, 0, 0)) + +z.t = 2 +>z.t : Symbol(Zet.t, Decl(templateTagOnConstructorFunctions.js, 7, 10)) +>z : Symbol(z, Decl(templateTagOnConstructorFunctions.js, 18, 3)) +>t : Symbol(Zet.t, Decl(templateTagOnConstructorFunctions.js, 7, 10)) + +z.u = false +>z.u : Symbol(Zet.u, Decl(templateTagOnConstructorFunctions.js, 5, 17), Decl(templateTagOnConstructorFunctions.js, 14, 37)) +>z : Symbol(z, Decl(templateTagOnConstructorFunctions.js, 18, 3)) +>u : Symbol(Zet.u, Decl(templateTagOnConstructorFunctions.js, 5, 17), Decl(templateTagOnConstructorFunctions.js, 14, 37)) + diff --git a/tests/baselines/reference/jsdocTemplateConstructorFunction.types b/tests/baselines/reference/jsdocTemplateConstructorFunction.types new file mode 100644 index 0000000000000..f838534aa339f --- /dev/null +++ b/tests/baselines/reference/jsdocTemplateConstructorFunction.types @@ -0,0 +1,76 @@ +=== tests/cases/conformance/jsdoc/templateTagOnConstructorFunctions.js === +/** + * @template {T} + * @typedef {(t: T) => T} Id + * @param {T} t + */ +function Zet(t) { +>Zet : typeof Zet +>t : T + + /** @type {T} */ + this.u +>this.u : any +>this : any +>u : any + + this.t = t +>this.t = t : T +>this.t : any +>this : any +>t : any +>t : T +} +/** + * @param {T} v + * @param {Id} id + */ +Zet.prototype.add = function(v, id) { +>Zet.prototype.add = function(v, id) { this.u = v || this.t return id(this.u)} : (v: T, id: (t: T) => T) => T +>Zet.prototype.add : any +>Zet.prototype : any +>Zet : typeof Zet +>prototype : any +>add : any +>function(v, id) { this.u = v || this.t return id(this.u)} : (v: T, id: (t: T) => T) => T +>v : T +>id : (t: T) => T + + this.u = v || this.t +>this.u = v || this.t : T +>this.u : T +>this : Zet +>u : T +>v || this.t : T +>v : T +>this.t : T +>this : Zet +>t : T + + return id(this.u) +>id(this.u) : T +>id : (t: T) => T +>this.u : T +>this : Zet +>u : T +} +var z = new Zet(1) +>z : typeof Zet +>new Zet(1) : typeof Zet +>Zet : typeof Zet +>1 : 1 + +z.t = 2 +>z.t = 2 : 2 +>z.t : number +>z : typeof Zet +>t : number +>2 : 2 + +z.u = false +>z.u = false : false +>z.u : number +>z : typeof Zet +>u : number +>false : false + diff --git a/tests/cases/conformance/jsdoc/jsdocTemplateClass.ts b/tests/cases/conformance/jsdoc/jsdocTemplateClass.ts new file mode 100644 index 0000000000000..5cd0cde89be12 --- /dev/null +++ b/tests/cases/conformance/jsdoc/jsdocTemplateClass.ts @@ -0,0 +1,29 @@ +// @allowJs: true +// @checkJs: true +// @noEmit: true +// @Filename: templateTagOnClasses.js + +/** + * @template {T} + * @typedef {(t: T) => T} Id + */ +class Foo { + /** @typedef {(t: T) => T} Id2 */ + /** @param {T} x */ + constructor (x) { + this.a = x + } + /** + * + * @param {T} x + * @param {Id} y + * @param {Id2} alpha + * @return {T} + */ + foo(x, y, alpha) { + return alpha(y(x)) + } +} +var f = new Foo(1) +var g = new Foo(false) +f.a = g.a diff --git a/tests/cases/conformance/jsdoc/jsdocTemplateConstructorFunction.ts b/tests/cases/conformance/jsdoc/jsdocTemplateConstructorFunction.ts new file mode 100644 index 0000000000000..dc44afe040a4a --- /dev/null +++ b/tests/cases/conformance/jsdoc/jsdocTemplateConstructorFunction.ts @@ -0,0 +1,26 @@ +// @allowJs: true +// @checkJs: true +// @noEmit: true +// @Filename: templateTagOnConstructorFunctions.js + +/** + * @template {T} + * @typedef {(t: T) => T} Id + * @param {T} t + */ +function Zet(t) { + /** @type {T} */ + this.u + this.t = t +} +/** + * @param {T} v + * @param {Id} id + */ +Zet.prototype.add = function(v, id) { + this.u = v || this.t + return id(this.u) +} +var z = new Zet(1) +z.t = 2 +z.u = false diff --git a/tests/cases/fourslash/findAllRefsJsDocTemplateTag_class_js.ts b/tests/cases/fourslash/findAllRefsJsDocTemplateTag_class_js.ts index 6577cfce15d3f..8ec2e86fadb92 100644 --- a/tests/cases/fourslash/findAllRefsJsDocTemplateTag_class_js.ts +++ b/tests/cases/fourslash/findAllRefsJsDocTemplateTag_class_js.ts @@ -3,15 +3,14 @@ // @allowJs: true // @Filename: /a.js -// TODO: https://github.com/Microsoft/TypeScript/issues/16411 // Both uses of T should be referenced. /////** @template [|{| "isWriteAccess": true, "isDefinition": true |}T|] */ ////class C { //// constructor() { -//// /** @type {T} */ +//// /** @type {[|T|]} */ //// this.x = null; //// } ////} -verify.singleReferenceGroup("(type parameter) T in C"); +verify.singleReferenceGroup("(type parameter) T in C", test.ranges());