Skip to content

Jsdoc @template in scope as type parameter #16463

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 5 commits into from
Jun 12, 2017
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
30 changes: 10 additions & 20 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6178,24 +6178,16 @@ namespace ts {
return undefined;
}

function getTypeParametersFromJSDocTemplate(declaration: SignatureDeclaration): TypeParameter[] {
if (declaration.flags & NodeFlags.JavaScriptFile) {
const templateTag = getJSDocTemplateTag(declaration);
if (templateTag) {
return getTypeParametersFromDeclaration(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[] = [];
forEach(typeParameterDeclarations, node => {
function getTypeParametersFromDeclaration(declaration: DeclarationWithTypeParameters): TypeParameter[] {
let result: TypeParameter[];
forEach(getEffectiveTypeParameterDeclarations(declaration), node => {
const tp = getDeclaredTypeOfTypeParameter(node.symbol);
if (!contains(result, tp)) {
if (!result) {
result = [];
}
result.push(tp);
}
});
Expand Down Expand Up @@ -6391,9 +6383,7 @@ namespace ts {
const classType = declaration.kind === SyntaxKind.Constructor ?
getDeclaredTypeOfClassOrInterface(getMergedSymbol((<ClassDeclaration>declaration.parent).symbol))
: undefined;
const typeParameters = classType ? classType.localTypeParameters :
declaration.typeParameters ? 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) :
Expand Down Expand Up @@ -8167,9 +8157,9 @@ 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 = getEffectiveTypeParameterDeclarations(node as DeclarationWithTypeParameters);
if (typeParameters) {
for (const d of typeParameters) {
if (contains(mappedTypes, getDeclaredTypeOfTypeParameter(getSymbolOfNode(d)))) {
return true;
}
Expand Down
16 changes: 15 additions & 1 deletion src/compiler/utilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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];
}
Expand Down Expand Up @@ -2743,6 +2743,20 @@ 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): 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.
Expand Down
36 changes: 36 additions & 0 deletions tests/baselines/reference/jsdocTemplateTag.symbols
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
=== tests/cases/conformance/jsdoc/jsdocTemplateTag.ts ===
/**
* @param {T} a
* @template T
*/
function f<T>(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<T>(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))

44 changes: 44 additions & 0 deletions tests/baselines/reference/jsdocTemplateTag.types
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
=== tests/cases/conformance/jsdoc/jsdocTemplateTag.ts ===
/**
* @param {T} a
* @template T
*/
function f<T>(a: T) {
>f : <T>(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 : <T>(a: T) => () => T
>1 : 1

/**
* @param {T} a
* @template T
* @returns {function(): T}
*/
function g<T>(a: T) {
>g : <T>(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 : <T>(a: T) => () => T
>'hi' : "hi"

21 changes: 21 additions & 0 deletions tests/cases/conformance/jsdoc/jsdocTemplateTag.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// @allowJs: true
// @checkJs: true
// @noEmit: true
/**
* @param {T} a
* @template T
*/
function f<T>(a: T) {
return () => a
}
let n = f(1)()

/**
* @param {T} a
* @template T
* @returns {function(): T}
*/
function g<T>(a: T) {
return () => a
}
let s = g('hi')()