Skip to content

Commit 3a33884

Browse files
Add support for converting JSDoc types to our own types.
1 parent 2e58435 commit 3a33884

File tree

1 file changed

+141
-0
lines changed

1 file changed

+141
-0
lines changed

src/compiler/checker.ts

+141
Original file line numberDiff line numberDiff line change
@@ -2105,8 +2105,75 @@ module ts {
21052105
return type;
21062106
}
21072107

2108+
function getTypeForVariableLikeDeclarationFromJSDocComment(declaration: VariableLikeDeclaration) {
2109+
// First see if this node has a doc comment on it directly.
2110+
let jsDocType = getJSDocTypeForVariableLikeDeclarationFromJSDocComment(declaration);
2111+
return getTypeFromJSDocType(jsDocType);
2112+
}
2113+
2114+
function getJSDocTypeForVariableLikeDeclarationFromJSDocComment(declaration: VariableLikeDeclaration): JSDocType {
2115+
// First, see if this node has an @type annotation on it directly.
2116+
let sourceFile = getSourceFileOfNode(declaration);
2117+
let docComment = getJSDocComment(declaration, sourceFile);
2118+
2119+
if (docComment && docComment.type) {
2120+
return docComment.type;
2121+
}
2122+
2123+
if (declaration.kind === SyntaxKind.VariableDeclaration &&
2124+
declaration.parent.kind === SyntaxKind.VariableDeclarationList &&
2125+
declaration.parent.parent.kind === SyntaxKind.VariableStatement) {
2126+
2127+
// @type annotation might have been on the variable statement, try that instead.
2128+
docComment = getJSDocComment(declaration.parent.parent, sourceFile);
2129+
if (docComment && docComment.type) {
2130+
return docComment.type;
2131+
}
2132+
}
2133+
else if (declaration.kind === SyntaxKind.Parameter && (<ParameterDeclaration>declaration).name.kind === SyntaxKind.Identifier) {
2134+
// If it's a parameter, see if the parent has a jsdoc comment with an @param
2135+
// annotation.
2136+
let parameterName = (<Identifier>(<ParameterDeclaration>declaration).name).text;
2137+
2138+
docComment = getJSDocComment(declaration.parent, sourceFile);
2139+
if (docComment && docComment.parameters) {
2140+
for (let parameter of docComment.parameters) {
2141+
if (parameter.name === parameterName) {
2142+
return parameter.type;
2143+
}
2144+
}
2145+
}
2146+
}
2147+
2148+
return undefined;
2149+
}
2150+
2151+
function getJSDocComment(node: Node, sourceFile: SourceFile) {
2152+
let comments = getLeadingCommentRangesOfNode(node, sourceFile);
2153+
if (comments) {
2154+
for (let comment of comments) {
2155+
let jsDocComment = parseJSDocComment(sourceFile.text, comment.pos, comment.end - comment.pos);
2156+
if (jsDocComment) {
2157+
return jsDocComment;
2158+
}
2159+
}
2160+
}
2161+
2162+
return undefined;
2163+
}
2164+
21082165
// Return the inferred type for a variable, parameter, or property declaration
21092166
function getTypeForVariableLikeDeclaration(declaration: VariableLikeDeclaration): Type {
2167+
if (declaration.parserContextFlags & ParserContextFlags.JavaScriptFile) {
2168+
// If this is a variable in a JavaScript file, then use the JSDoc type (if it has
2169+
// one as it's type), otherwise fallback to the below standard TS codepaths to
2170+
// try to figure it out.
2171+
let type = getTypeForVariableLikeDeclarationFromJSDocComment(declaration);
2172+
if (type) {
2173+
return type;
2174+
}
2175+
}
2176+
21102177
// A variable declared in a for..in statement is always of type any
21112178
if (declaration.parent.parent.kind === SyntaxKind.ForInStatement) {
21122179
return anyType;
@@ -2121,6 +2188,7 @@ module ts {
21212188
if (isBindingPattern(declaration.parent)) {
21222189
return getTypeForBindingElement(<BindingElement>declaration);
21232190
}
2191+
21242192
// Use type from type annotation if one is present
21252193
if (declaration.type) {
21262194
return getTypeFromTypeNodeOrHeritageClauseElement(declaration.type);
@@ -3618,6 +3686,79 @@ module ts {
36183686
return links.resolvedType;
36193687
}
36203688

3689+
function getTypeFromJSDocType(jsDocType: JSDocType): Type {
3690+
if (jsDocType) {
3691+
switch (jsDocType.kind) {
3692+
case SyntaxKind.JSDocAllType:
3693+
return anyType;
3694+
case SyntaxKind.JSDocUnknownType:
3695+
return unknownType;
3696+
case SyntaxKind.JSDocUnionType:
3697+
return getTypeFromJSDocUnionType(<JSDocUnionType>jsDocType);
3698+
case SyntaxKind.JSDocNullableType:
3699+
return getTypeFromJSDocType((<JSDocNullableType>jsDocType).type);
3700+
case SyntaxKind.JSDocNonNullableType:
3701+
return getTypeFromJSDocType((<JSDocNonNullableType>jsDocType).type);
3702+
case SyntaxKind.JSDocRecordType:
3703+
// NYI:
3704+
break;
3705+
case SyntaxKind.JSDocTypeReference:
3706+
return getTypeForJSDocTypeReference(<JSDocTypeReference>jsDocType);
3707+
case SyntaxKind.JSDocOptionalType:
3708+
return getTypeFromJSDocType((<JSDocOptionalType>jsDocType).type);
3709+
case SyntaxKind.JSDocFunctionType:
3710+
// NYI:
3711+
break;
3712+
case SyntaxKind.JSDocVariadicType:
3713+
return getTypeFromJSDocType((<JSDocVariadicType>jsDocType).type);
3714+
case SyntaxKind.JSDocConstructorType:
3715+
// NYI:
3716+
break;
3717+
case SyntaxKind.JSDocThisType:
3718+
// NYI:
3719+
break;
3720+
}
3721+
}
3722+
}
3723+
3724+
function getTypeForJSDocTypeReference(node: JSDocTypeReference): Type {
3725+
if (node.name.kind === SyntaxKind.Identifier) {
3726+
switch ((<Identifier>node.name).text) {
3727+
case "any": return anyType;
3728+
case "boolean": return booleanType;
3729+
case "number": return numberType;
3730+
case "string": return stringType;
3731+
case "symbol": return esSymbolType;
3732+
}
3733+
}
3734+
3735+
let symbol = resolveEntityName(node.name, SymbolFlags.Type);
3736+
if (symbol) {
3737+
let type = getDeclaredTypeOfSymbol(symbol);
3738+
if (type.flags & (TypeFlags.Class | TypeFlags.Interface) && type.flags & TypeFlags.Reference) {
3739+
let typeParameters = (<InterfaceType>type).typeParameters;
3740+
if (node.typeArguments && node.typeArguments.length === typeParameters.length) {
3741+
let typeArguments = map(node.typeArguments, getTypeFromJSDocType);
3742+
for (let typeArgument in typeArguments) {
3743+
if (!typeArgument) {
3744+
return undefined;
3745+
}
3746+
}
3747+
3748+
type = createTypeReference(<GenericType>type, typeArguments);
3749+
}
3750+
}
3751+
3752+
return type;
3753+
}
3754+
3755+
return undefined;
3756+
}
3757+
3758+
function getTypeFromJSDocUnionType(node: JSDocUnionType): Type {
3759+
return getUnionType(map(node.types, getTypeFromJSDocType), /*noSubtypeReduction*/ true);
3760+
}
3761+
36213762
function getTypeFromTypeNodeOrHeritageClauseElement(node: TypeNode | LiteralExpression | HeritageClauseElement): Type {
36223763
switch (node.kind) {
36233764
case SyntaxKind.AnyKeyword:

0 commit comments

Comments
 (0)