Improve contextual types using jsdoc tags

This commit is contained in:
Ron Buckton 2017-06-07 17:14:27 -07:00
parent 43e3d60f09
commit 6bbacb64ce
7 changed files with 80 additions and 17 deletions

View file

@ -12744,6 +12744,10 @@ namespace ts {
if (declaration.type) {
return getTypeFromTypeNode(declaration.type);
}
const jsDocType = isInJavaScriptFile(declaration) && getTypeForDeclarationFromJSDocComment(declaration);
if (jsDocType) {
return jsDocType;
}
if (declaration.kind === SyntaxKind.Parameter) {
const type = getContextuallyTypedParameterType(<ParameterDeclaration>declaration);
if (type) {
@ -12816,8 +12820,9 @@ namespace ts {
// If the containing function has a return type annotation, is a constructor, or is a get accessor whose
// corresponding set accessor has a type annotation, return statements in the function are contextually typed
if (functionDecl.type ||
(isInJavaScriptFile(functionDecl) && getJSDocReturnType(functionDecl)) ||
functionDecl.kind === SyntaxKind.Constructor ||
functionDecl.kind === SyntaxKind.GetAccessor && getSetAccessorTypeAnnotationNode(getDeclarationOfKind<SetAccessorDeclaration>(functionDecl.symbol, SyntaxKind.SetAccessor))) {
functionDecl.kind === SyntaxKind.GetAccessor && getSetAccessorTypeAnnotationNode(getDeclarationOfKind<SetAccessorDeclaration>(functionDecl.symbol, SyntaxKind.SetAccessor), /*includeJSDocType*/ true)) {
return getReturnTypeOfSignature(getSignatureFromDeclaration(functionDecl));
}
@ -16437,11 +16442,10 @@ namespace ts {
}
function getReturnTypeFromJSDocComment(func: SignatureDeclaration | FunctionDeclaration): Type {
const returnTag = getJSDocReturnTag(func);
if (returnTag && returnTag.typeExpression) {
return getTypeFromTypeNode(returnTag.typeExpression.type);
const jsdocType = getJSDocReturnType(func);
if (jsdocType) {
return getTypeFromTypeNode(jsdocType);
}
return undefined;
}

View file

@ -1570,6 +1570,11 @@ namespace ts {
return getFirstJSDocTag(node, SyntaxKind.JSDocReturnTag) as JSDocReturnTag;
}
export function getJSDocReturnType(node: Node): JSDocType {
const returnTag = getJSDocReturnTag(node);
return returnTag && returnTag.typeExpression && returnTag.typeExpression.type;
}
export function getJSDocTemplateTag(node: Node): JSDocTemplateTag {
return getFirstJSDocTag(node, SyntaxKind.JSDocTemplateTag) as JSDocTemplateTag;
}
@ -2615,11 +2620,19 @@ namespace ts {
});
}
/** Get the type annotaion for the value parameter. */
export function getSetAccessorTypeAnnotationNode(accessor: SetAccessorDeclaration): TypeNode {
/** Get the type annotation for the value parameter. */
export function getSetAccessorTypeAnnotationNode(accessor: SetAccessorDeclaration, includeJSDocType?: boolean): TypeNode {
if (accessor && accessor.parameters.length > 0) {
const hasThis = accessor.parameters.length === 2 && parameterIsThisKeyword(accessor.parameters[0]);
return accessor.parameters[hasThis ? 1 : 0].type;
const parameter = accessor.parameters[hasThis ? 1 : 0];
if (parameter) {
if (parameter.type) {
return parameter.type;
}
if (includeJSDocType && parameter.flags & NodeFlags.JavaScriptFile) {
return getJSDocType(parameter);
}
}
}
}

View file

@ -61,10 +61,10 @@ x(1);
/** @type {function (number)} */
const x1 = (a) => a + 1;
>x1 : (arg0: number) => any
>(a) => a + 1 : (a: any) => any
>a : any
>a + 1 : any
>a : any
>(a) => a + 1 : (a: number) => number
>a : number
>a + 1 : number
>a : number
>1 : 1
x1(0);
@ -75,10 +75,10 @@ x1(0);
/** @type {function (number): number} */
const x2 = (a) => a + 1;
>x2 : (arg0: number) => number
>(a) => a + 1 : (a: any) => any
>a : any
>a + 1 : any
>a : any
>(a) => a + 1 : (a: number) => number
>a : number
>a + 1 : number
>a : number
>1 : 1
x2(0);

View file

@ -4,9 +4,10 @@ tests/cases/conformance/jsdoc/0.js(10,4): error TS2345: Argument of type '"strin
tests/cases/conformance/jsdoc/0.js(13,7): error TS2451: Cannot redeclare block-scoped variable 'x2'.
tests/cases/conformance/jsdoc/0.js(17,1): error TS2322: Type 'number' is not assignable to type 'string'.
tests/cases/conformance/jsdoc/0.js(20,7): error TS2451: Cannot redeclare block-scoped variable 'x2'.
tests/cases/conformance/jsdoc/0.js(20,21): error TS2339: Property 'concat' does not exist on type 'number'.
==== tests/cases/conformance/jsdoc/0.js (6 errors) ====
==== tests/cases/conformance/jsdoc/0.js (7 errors) ====
// @ts-check
/** @type {String} */
var S = true;
@ -39,4 +40,6 @@ tests/cases/conformance/jsdoc/0.js(20,7): error TS2451: Cannot redeclare block-s
const x2 = (a) => a.concat("hi");
~~
!!! error TS2451: Cannot redeclare block-scoped variable 'x2'.
~~~~~~
!!! error TS2339: Property 'concat' does not exist on type 'number'.
x2(0);

View file

@ -0,0 +1,12 @@
=== tests/cases/conformance/types/contextualTypes/jsdoc/index.js ===
/** @type {Array<[string, {x?:number, y?:number}]>} */
const arr = [
>arr : Symbol(arr, Decl(index.js, 1, 5))
['a', { x: 1 }],
>x : Symbol(x, Decl(index.js, 2, 11))
['b', { y: 2 }]
>y : Symbol(y, Decl(index.js, 3, 11))
];

View file

@ -0,0 +1,21 @@
=== tests/cases/conformance/types/contextualTypes/jsdoc/index.js ===
/** @type {Array<[string, {x?:number, y?:number}]>} */
const arr = [
>arr : [string, { x?: number; y?: number; }][]
>[ ['a', { x: 1 }], ['b', { y: 2 }]] : ([string, { x: number; }] | [string, { y: number; }])[]
['a', { x: 1 }],
>['a', { x: 1 }] : [string, { x: number; }]
>'a' : "a"
>{ x: 1 } : { x: number; }
>x : number
>1 : 1
['b', { y: 2 }]
>['b', { y: 2 }] : [string, { y: number; }]
>'b' : "b"
>{ y: 2 } : { y: number; }
>y : number
>2 : 2
];

View file

@ -0,0 +1,10 @@
// @allowJs: true
// @checkJs: true
// @noEmit: true
// @filename: index.js
/** @type {Array<[string, {x?:number, y?:number}]>} */
const arr = [
['a', { x: 1 }],
['b', { y: 2 }]
];