Improve contextual types using jsdoc tags
This commit is contained in:
parent
43e3d60f09
commit
6bbacb64ce
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
12
tests/baselines/reference/contextualTypeFromJSDoc.symbols
Normal file
12
tests/baselines/reference/contextualTypeFromJSDoc.symbols
Normal 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))
|
||||
|
||||
];
|
21
tests/baselines/reference/contextualTypeFromJSDoc.types
Normal file
21
tests/baselines/reference/contextualTypeFromJSDoc.types
Normal 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
|
||||
|
||||
];
|
|
@ -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 }]
|
||||
];
|
Loading…
Reference in a new issue