From 1b585dd503b450191624185b35d8a13c2b204e9b Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Mon, 12 Jun 2017 13:55:07 -0700 Subject: [PATCH 1/5] Type params introduced by @template are in scope The test to make sure that type parameters are in scope for instantiation previously ignored type parameters created by `@template`. Now it correctly says that they are in scope. --- src/compiler/checker.ts | 22 +++++++++++----------- src/compiler/utilities.ts | 2 +- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index bbd38ddbc5ded..47828088daf03 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -6178,24 +6178,24 @@ namespace ts { return undefined; } - function getTypeParametersFromJSDocTemplate(declaration: SignatureDeclaration): TypeParameter[] { + function getTypeParametersFromJSDocTemplate(declaration: SignatureDeclaration): TypeParameterDeclaration[] { if (declaration.flags & NodeFlags.JavaScriptFile) { const templateTag = getJSDocTemplateTag(declaration); - if (templateTag) { - return getTypeParametersFromDeclaration(templateTag.typeParameters); - } + return templateTag && templateTag.typeParameters; } - return undefined; } // Return list of type parameters with duplicates removed (duplicate identifier errors are generated in the actual // type checking functions). function getTypeParametersFromDeclaration(typeParameterDeclarations: TypeParameterDeclaration[]): TypeParameter[] { - const result: TypeParameter[] = []; + let result: TypeParameter[]; forEach(typeParameterDeclarations, node => { const tp = getDeclaredTypeOfTypeParameter(node.symbol); if (!contains(result, tp)) { + if (!result) { + result = []; + } result.push(tp); } }); @@ -6392,8 +6392,7 @@ namespace ts { getDeclaredTypeOfClassOrInterface(getMergedSymbol((declaration.parent).symbol)) : undefined; const typeParameters = classType ? classType.localTypeParameters : - declaration.typeParameters ? getTypeParametersFromDeclaration(declaration.typeParameters) : - getTypeParametersFromJSDocTemplate(declaration); + getTypeParametersFromDeclaration(declaration.typeParameters || getTypeParametersFromJSDocTemplate(declaration)); const returnType = getSignatureReturnTypeFromDeclaration(declaration, isJSConstructSignature, classType); const typePredicate = declaration.type && declaration.type.kind === SyntaxKind.TypePredicate ? createTypePredicateFromTypePredicateNode(declaration.type as TypePredicateNode) : @@ -8167,9 +8166,10 @@ namespace ts { case SyntaxKind.ClassExpression: case SyntaxKind.InterfaceDeclaration: case SyntaxKind.TypeAliasDeclaration: - const declaration = node as DeclarationWithTypeParameters; - if (declaration.typeParameters) { - for (const d of declaration.typeParameters) { + const typeParameters = (node as DeclarationWithTypeParameters).typeParameters || + getTypeParametersFromJSDocTemplate(node as SignatureDeclaration); + if (typeParameters) { + for (const d of typeParameters) { if (contains(mappedTypes, getDeclaredTypeOfTypeParameter(getSymbolOfNode(d)))) { return true; } diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index cee2e3626adb3..07a1db7c53530 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -1448,7 +1448,7 @@ namespace ts { return node && firstOrUndefined(getJSDocTags(node, kind)); } - export function getJSDocs(node: Node): (JSDoc | JSDocTag)[] { + export function getJSDocs(node: Node): (JSDoc | JSDocTag)[] { if (isJSDocTypedefTag(node)) { return [node.parent]; } From da83eb967aa0b7cd1b30995eca18dead12c71e17 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Mon, 12 Jun 2017 13:56:17 -0700 Subject: [PATCH 2/5] Returned generic function is instantiated correctly --- .../reference/jsdocTemplateTag.symbols | 36 +++++++++++++++ .../reference/jsdocTemplateTag.types | 44 +++++++++++++++++++ .../conformance/jsdoc/jsdocTemplateTag.ts | 21 +++++++++ 3 files changed, 101 insertions(+) create mode 100644 tests/baselines/reference/jsdocTemplateTag.symbols create mode 100644 tests/baselines/reference/jsdocTemplateTag.types create mode 100644 tests/cases/conformance/jsdoc/jsdocTemplateTag.ts diff --git a/tests/baselines/reference/jsdocTemplateTag.symbols b/tests/baselines/reference/jsdocTemplateTag.symbols new file mode 100644 index 0000000000000..699e93339edbc --- /dev/null +++ b/tests/baselines/reference/jsdocTemplateTag.symbols @@ -0,0 +1,36 @@ +=== tests/cases/conformance/jsdoc/jsdocTemplateTag.ts === +/** + * @param {T} a + * @template T + */ +function f(a: T) { +>f : Symbol(f, Decl(jsdocTemplateTag.ts, 0, 0)) +>T : Symbol(T, Decl(jsdocTemplateTag.ts, 4, 11)) +>a : Symbol(a, Decl(jsdocTemplateTag.ts, 4, 14)) +>T : Symbol(T, Decl(jsdocTemplateTag.ts, 4, 11)) + + return () => a +>a : Symbol(a, Decl(jsdocTemplateTag.ts, 4, 14)) +} +let n = f(1)() +>n : Symbol(n, Decl(jsdocTemplateTag.ts, 7, 3)) +>f : Symbol(f, Decl(jsdocTemplateTag.ts, 0, 0)) + +/** + * @param {T} a + * @template T + * @returns {function(): T} + */ +function g(a: T) { +>g : Symbol(g, Decl(jsdocTemplateTag.ts, 7, 14)) +>T : Symbol(T, Decl(jsdocTemplateTag.ts, 14, 11)) +>a : Symbol(a, Decl(jsdocTemplateTag.ts, 14, 14)) +>T : Symbol(T, Decl(jsdocTemplateTag.ts, 14, 11)) + + return () => a +>a : Symbol(a, Decl(jsdocTemplateTag.ts, 14, 14)) +} +let s = g('hi')() +>s : Symbol(s, Decl(jsdocTemplateTag.ts, 17, 3)) +>g : Symbol(g, Decl(jsdocTemplateTag.ts, 7, 14)) + diff --git a/tests/baselines/reference/jsdocTemplateTag.types b/tests/baselines/reference/jsdocTemplateTag.types new file mode 100644 index 0000000000000..72b0529d97efe --- /dev/null +++ b/tests/baselines/reference/jsdocTemplateTag.types @@ -0,0 +1,44 @@ +=== tests/cases/conformance/jsdoc/jsdocTemplateTag.ts === +/** + * @param {T} a + * @template T + */ +function f(a: T) { +>f : (a: T) => () => T +>T : T +>a : T +>T : T + + return () => a +>() => a : () => T +>a : T +} +let n = f(1)() +>n : number +>f(1)() : number +>f(1) : () => number +>f : (a: T) => () => T +>1 : 1 + +/** + * @param {T} a + * @template T + * @returns {function(): T} + */ +function g(a: T) { +>g : (a: T) => () => T +>T : T +>a : T +>T : T + + return () => a +>() => a : () => T +>a : T +} +let s = g('hi')() +>s : string +>g('hi')() : string +>g('hi') : () => string +>g : (a: T) => () => T +>'hi' : "hi" + diff --git a/tests/cases/conformance/jsdoc/jsdocTemplateTag.ts b/tests/cases/conformance/jsdoc/jsdocTemplateTag.ts new file mode 100644 index 0000000000000..42b21cf935bf7 --- /dev/null +++ b/tests/cases/conformance/jsdoc/jsdocTemplateTag.ts @@ -0,0 +1,21 @@ +// @allowJs: true +// @checkJs: true +// @noEmit: true +/** + * @param {T} a + * @template T + */ +function f(a: T) { + return () => a +} +let n = f(1)() + +/** + * @param {T} a + * @template T + * @returns {function(): T} + */ +function g(a: T) { + return () => a +} +let s = g('hi')() From eda7978dd198cee58cc3ff206dc3c25ff8bec3c6 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Mon, 12 Jun 2017 14:06:46 -0700 Subject: [PATCH 3/5] Cleanup getTypeParametersFromDeclaration et al --- src/compiler/checker.ts | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 47828088daf03..d800d7ec3e321 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -6178,7 +6178,7 @@ namespace ts { return undefined; } - function getTypeParametersFromJSDocTemplate(declaration: SignatureDeclaration): TypeParameterDeclaration[] { + function getTypeParametersFromJSDocTemplate(declaration: DeclarationWithTypeParameters): TypeParameterDeclaration[] { if (declaration.flags & NodeFlags.JavaScriptFile) { const templateTag = getJSDocTemplateTag(declaration); return templateTag && templateTag.typeParameters; @@ -6188,9 +6188,9 @@ namespace ts { // Return list of type parameters with duplicates removed (duplicate identifier errors are generated in the actual // type checking functions). - function getTypeParametersFromDeclaration(typeParameterDeclarations: TypeParameterDeclaration[]): TypeParameter[] { + function getTypeParametersFromDeclaration(declaration: DeclarationWithTypeParameters): TypeParameter[] { let result: TypeParameter[]; - forEach(typeParameterDeclarations, node => { + forEach(declaration.typeParameters || getTypeParametersFromJSDocTemplate(declaration), node => { const tp = getDeclaredTypeOfTypeParameter(node.symbol); if (!contains(result, tp)) { if (!result) { @@ -6391,8 +6391,7 @@ namespace ts { const classType = declaration.kind === SyntaxKind.Constructor ? getDeclaredTypeOfClassOrInterface(getMergedSymbol((declaration.parent).symbol)) : undefined; - const typeParameters = classType ? classType.localTypeParameters : - getTypeParametersFromDeclaration(declaration.typeParameters || getTypeParametersFromJSDocTemplate(declaration)); + const typeParameters = classType ? classType.localTypeParameters : getTypeParametersFromDeclaration(declaration); const returnType = getSignatureReturnTypeFromDeclaration(declaration, isJSConstructSignature, classType); const typePredicate = declaration.type && declaration.type.kind === SyntaxKind.TypePredicate ? createTypePredicateFromTypePredicateNode(declaration.type as TypePredicateNode) : @@ -8166,8 +8165,8 @@ namespace ts { case SyntaxKind.ClassExpression: case SyntaxKind.InterfaceDeclaration: case SyntaxKind.TypeAliasDeclaration: - const typeParameters = (node as DeclarationWithTypeParameters).typeParameters || - getTypeParametersFromJSDocTemplate(node as SignatureDeclaration); + const declaration = node as DeclarationWithTypeParameters; + const typeParameters = declaration.typeParameters || getTypeParametersFromJSDocTemplate(declaration); if (typeParameters) { for (const d of typeParameters) { if (contains(mappedTypes, getDeclaredTypeOfTypeParameter(getSymbolOfNode(d)))) { From abc9e687ac7affb9ac9e94f0619fb87831cec97f Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Mon, 12 Jun 2017 14:23:47 -0700 Subject: [PATCH 4/5] Consolidate getting type parameter declarations Create getEffectiveTypeParameterDeclarations in utilities.ts --- src/compiler/checker.ts | 13 ++----------- src/compiler/utilities.ts | 14 ++++++++++++++ 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index d800d7ec3e321..b7b9b6c38040b 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -6178,19 +6178,11 @@ namespace ts { return undefined; } - function getTypeParametersFromJSDocTemplate(declaration: DeclarationWithTypeParameters): TypeParameterDeclaration[] { - if (declaration.flags & NodeFlags.JavaScriptFile) { - const templateTag = getJSDocTemplateTag(declaration); - return templateTag && templateTag.typeParameters; - } - return undefined; - } - // Return list of type parameters with duplicates removed (duplicate identifier errors are generated in the actual // type checking functions). function getTypeParametersFromDeclaration(declaration: DeclarationWithTypeParameters): TypeParameter[] { let result: TypeParameter[]; - forEach(declaration.typeParameters || getTypeParametersFromJSDocTemplate(declaration), node => { + forEach(getEffectiveTypeParameterDeclarations(declaration), node => { const tp = getDeclaredTypeOfTypeParameter(node.symbol); if (!contains(result, tp)) { if (!result) { @@ -8165,8 +8157,7 @@ namespace ts { case SyntaxKind.ClassExpression: case SyntaxKind.InterfaceDeclaration: case SyntaxKind.TypeAliasDeclaration: - const declaration = node as DeclarationWithTypeParameters; - const typeParameters = declaration.typeParameters || getTypeParametersFromJSDocTemplate(declaration); + const typeParameters = getEffectiveTypeParameterDeclarations(node as DeclarationWithTypeParameters); if (typeParameters) { for (const d of typeParameters) { if (contains(mappedTypes, getDeclaredTypeOfTypeParameter(getSymbolOfNode(d)))) { diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 07a1db7c53530..dc9b9ffb8a836 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -2743,6 +2743,20 @@ namespace ts { } } + /** + * Gets the effective return type annotation of a signature. If the node was parsed in a + * JavaScript file, gets the return type annotation from JSDoc. + */ + export function getEffectiveTypeParameterDeclarations(node: DeclarationWithTypeParameters): TypeParameterDeclaration[] { + if (node.typeParameters) { + return node.typeParameters; + } + if (node.flags & NodeFlags.JavaScriptFile) { + const templateTag = getJSDocTemplateTag(node); + return templateTag && templateTag.typeParameters; + } + } + /** * Gets the effective type annotation of the value parameter of a set accessor. If the node * was parsed in a JavaScript file, gets the type annotation from JSDoc. From 024ab094b3194e3a60e90de9b1a3e68ecd1700e3 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Mon, 12 Jun 2017 14:27:43 -0700 Subject: [PATCH 5/5] Update jsdoc of new function --- src/compiler/utilities.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index dc9b9ffb8a836..7b7fb92ccc5b4 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -2744,8 +2744,8 @@ namespace ts { } /** - * Gets the effective return type annotation of a signature. If the node was parsed in a - * JavaScript file, gets the return type annotation from JSDoc. + * 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): TypeParameterDeclaration[] { if (node.typeParameters) {