Merge pull request #16463 from Microsoft/jsdoc-@template-in-scope-as-type-parameter

Jsdoc @template in scope as type parameter
This commit is contained in:
Nathan Shively-Sanders 2017-06-12 16:07:49 -07:00 committed by GitHub
commit 3d8cf62846
5 changed files with 126 additions and 21 deletions

View File

@ -6170,24 +6170,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);
}
});
@ -6383,9 +6375,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) :
@ -8166,9 +8156,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;
}

View File

@ -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];
}
@ -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.

View File

@ -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))

View File

@ -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"

View File

@ -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')()