Skip to content

Commit fc933d7

Browse files
committed
Transform jsdoc types in the refactor, not emitter
The emitter now understands JSDoc types but emits them in the original format.
1 parent 96b8093 commit fc933d7

3 files changed

Lines changed: 276 additions & 30 deletions

File tree

src/compiler/emitter.ts

Lines changed: 25 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -545,8 +545,9 @@ namespace ts {
545545
case SyntaxKind.TypeReference:
546546
return emitTypeReference(<TypeReferenceNode>node);
547547
case SyntaxKind.FunctionType:
548-
case SyntaxKind.JSDocFunctionType:
549548
return emitFunctionType(<FunctionTypeNode>node);
549+
case SyntaxKind.JSDocFunctionType:
550+
return emitJSDocFunctionType(node as JSDocFunctionType);
550551
case SyntaxKind.ConstructorType:
551552
return emitConstructorType(<ConstructorTypeNode>node);
552553
case SyntaxKind.TypeQuery:
@@ -576,8 +577,10 @@ namespace ts {
576577
case SyntaxKind.LiteralType:
577578
return emitLiteralType(<LiteralTypeNode>node);
578579
case SyntaxKind.JSDocAllType:
580+
write("*");
581+
break;
579582
case SyntaxKind.JSDocUnknownType:
580-
write("any");
583+
write("?");
581584
break;
582585
case SyntaxKind.JSDocNullableType:
583586
return emitJSDocNullableType(node as JSDocNullableType);
@@ -930,14 +933,13 @@ namespace ts {
930933
if (node.name) {
931934
emit(node.name);
932935
}
933-
else if (node.parent.kind === SyntaxKind.JSDocFunctionType) {
934-
const i = (node.parent as JSDocFunctionType).parameters.indexOf(node);
935-
if (i > -1) {
936-
write("arg" + i);
937-
}
938-
}
939936
emitIfPresent(node.questionToken);
940-
emitWithPrefix(": ", node.type);
937+
if (node.parent && node.parent.kind === SyntaxKind.JSDocFunctionType && !node.name) {
938+
emit(node.type);
939+
}
940+
else {
941+
emitWithPrefix(": ", node.type);
942+
}
941943
emitExpressionWithPrefix(" = ", node.initializer);
942944
}
943945

@@ -1056,18 +1058,27 @@ namespace ts {
10561058
emit(node.type);
10571059
}
10581060

1061+
function emitJSDocFunctionType(node: JSDocFunctionType) {
1062+
write("function");
1063+
emitParameters(node, node.parameters);
1064+
write(":");
1065+
emit(node.type);
1066+
}
1067+
1068+
10591069
function emitJSDocNullableType(node: JSDocNullableType) {
1070+
write("?");
10601071
emit(node.type);
1061-
write(" | null");
10621072
}
10631073

10641074
function emitJSDocNonNullableType(node: JSDocNonNullableType) {
1075+
write("!");
10651076
emit(node.type);
10661077
}
10671078

10681079
function emitJSDocOptionalType(node: JSDocOptionalType) {
10691080
emit(node.type);
1070-
write(" | undefined");
1081+
write("=");
10711082
}
10721083

10731084
function emitConstructorType(node: ConstructorTypeNode) {
@@ -1096,8 +1107,8 @@ namespace ts {
10961107
}
10971108

10981109
function emitJSDocVariadicType(node: JSDocVariadicType) {
1110+
write("...");
10991111
emit(node.type);
1100-
write("[]");
11011112
}
11021113

11031114
function emitTupleType(node: TupleTypeNode) {
@@ -2397,7 +2408,7 @@ namespace ts {
23972408
emitList(parentNode, parameters, ListFormat.Parameters);
23982409
}
23992410

2400-
function canEmitSimpleArrowHead(parentNode: FunctionTypeNode | ArrowFunction | JSDocFunctionType, parameters: NodeArray<ParameterDeclaration>) {
2411+
function canEmitSimpleArrowHead(parentNode: FunctionTypeNode | ArrowFunction, parameters: NodeArray<ParameterDeclaration>) {
24012412
const parameter = singleOrUndefined(parameters);
24022413
return parameter
24032414
&& parameter.pos === parentNode.pos // may not have parsed tokens between parent and parameter
@@ -2414,7 +2425,7 @@ namespace ts {
24142425
&& isIdentifier(parameter.name); // parameter name must be identifier
24152426
}
24162427

2417-
function emitParametersForArrow(parentNode: FunctionTypeNode | ArrowFunction | JSDocFunctionType, parameters: NodeArray<ParameterDeclaration>) {
2428+
function emitParametersForArrow(parentNode: FunctionTypeNode | ArrowFunction, parameters: NodeArray<ParameterDeclaration>) {
24182429
if (canEmitSimpleArrowHead(parentNode, parameters)) {
24192430
emitList(parentNode, parameters, ListFormat.Parameters & ~ListFormat.Parenthesis);
24202431
}

src/services/refactors/annotateWithTypeFromJSDoc.ts

Lines changed: 93 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -35,16 +35,16 @@ namespace ts.refactor.annotateWithTypeFromJSDoc {
3535
if (decl && !decl.type) {
3636
const annotate = getJSDocType(decl) ? annotateTypeFromJSDoc :
3737
getJSDocReturnType(decl) ? annotateReturnTypeFromJSDoc :
38-
undefined;
38+
undefined;
3939
if (annotate) {
4040
return [{
4141
name: annotate.name,
4242
description: annotate.description,
4343
actions: [
4444
{
45-
description: annotate.description,
46-
name: actionName
47-
}
45+
description: annotate.description,
46+
name: actionName
47+
}
4848
]
4949
}];
5050
}
@@ -62,10 +62,9 @@ namespace ts.refactor.annotateWithTypeFromJSDoc {
6262
const sourceFile = context.file;
6363
const token = getTokenAtPosition(sourceFile, start, /*includeJsDocComment*/ false);
6464
const decl = findAncestor(token, isTypedNode);
65-
const jsdocType = getJSDocType(decl);
66-
const jsdocReturn = getJSDocReturnType(decl);
67-
if (!decl || !jsdocType && !jsdocReturn || decl.type) {
68-
Debug.fail(`!decl || !jsdocType && !jsdocReturn || decl.type: !${decl} || !${jsdocType} && !{jsdocReturn} || ${decl.type}`);
65+
const jsdocType = getJSDocReturnType(decl) || getJSDocType(decl);
66+
if (!decl || !jsdocType || decl.type) {
67+
Debug.fail(`!decl || !jsdocType || decl.type: !${decl} || !${jsdocType} || ${decl.type}`);
6968
return undefined;
7069
}
7170

@@ -76,12 +75,12 @@ namespace ts.refactor.annotateWithTypeFromJSDoc {
7675
// other syntax changes
7776
const arrow = decl.parent as ArrowFunction;
7877
const param = decl as ParameterDeclaration;
79-
const replacementParam = createParameter(param.decorators, param.modifiers, param.dotDotDotToken, param.name, param.questionToken, jsdocType, param.initializer);
78+
const replacementParam = createParameter(param.decorators, param.modifiers, param.dotDotDotToken, param.name, param.questionToken, transformJSDocType(jsdocType) as TypeNode, param.initializer);
8079
const replacement = createArrowFunction(arrow.modifiers, arrow.typeParameters, [replacementParam], arrow.type, arrow.equalsGreaterThanToken, arrow.body);
8180
changeTracker.replaceRange(sourceFile, { pos: arrow.getStart(), end: arrow.end }, replacement);
8281
}
8382
else {
84-
changeTracker.replaceRange(sourceFile, { pos: decl.getStart(), end: decl.end }, replaceType(decl, jsdocType, jsdocReturn));
83+
changeTracker.replaceRange(sourceFile, { pos: decl.getStart(), end: decl.end }, replaceType(decl, transformJSDocType(jsdocType) as TypeNode));
8584
}
8685
return {
8786
edits: changeTracker.getChanges(),
@@ -98,7 +97,7 @@ namespace ts.refactor.annotateWithTypeFromJSDoc {
9897
node.kind === SyntaxKind.PropertyDeclaration;
9998
}
10099

101-
function replaceType(decl: DeclarationWithType, jsdocType: TypeNode, jsdocReturn: TypeNode) {
100+
function replaceType(decl: DeclarationWithType, jsdocType: TypeNode) {
102101
switch (decl.kind) {
103102
case SyntaxKind.VariableDeclaration:
104103
return createVariableDeclaration(decl.name, jsdocType, decl.initializer);
@@ -109,15 +108,15 @@ namespace ts.refactor.annotateWithTypeFromJSDoc {
109108
case SyntaxKind.PropertyDeclaration:
110109
return createProperty(decl.decorators, decl.modifiers, decl.name, decl.questionToken, jsdocType, decl.initializer);
111110
case SyntaxKind.FunctionDeclaration:
112-
return createFunctionDeclaration(decl.decorators, decl.modifiers, decl.asteriskToken, decl.name, decl.typeParameters, decl.parameters, jsdocReturn, decl.body);
111+
return createFunctionDeclaration(decl.decorators, decl.modifiers, decl.asteriskToken, decl.name, decl.typeParameters, decl.parameters, jsdocType, decl.body);
113112
case SyntaxKind.FunctionExpression:
114-
return createFunctionExpression(decl.modifiers, decl.asteriskToken, decl.name, decl.typeParameters, decl.parameters, jsdocReturn, decl.body);
113+
return createFunctionExpression(decl.modifiers, decl.asteriskToken, decl.name, decl.typeParameters, decl.parameters, jsdocType, decl.body);
115114
case SyntaxKind.ArrowFunction:
116-
return createArrowFunction(decl.modifiers, decl.typeParameters, decl.parameters, jsdocReturn, decl.equalsGreaterThanToken, decl.body);
115+
return createArrowFunction(decl.modifiers, decl.typeParameters, decl.parameters, jsdocType, decl.equalsGreaterThanToken, decl.body);
117116
case SyntaxKind.MethodDeclaration:
118-
return createMethod(decl.decorators, decl.modifiers, decl.asteriskToken, decl.name, decl.questionToken, decl.typeParameters, decl.parameters, jsdocReturn, decl.body);
117+
return createMethod(decl.decorators, decl.modifiers, decl.asteriskToken, decl.name, decl.questionToken, decl.typeParameters, decl.parameters, jsdocType, decl.body);
119118
case SyntaxKind.GetAccessor:
120-
return createGetAccessor(decl.decorators, decl.modifiers, decl.name, decl.parameters, jsdocReturn, decl.body);
119+
return createGetAccessor(decl.decorators, decl.modifiers, decl.name, decl.parameters, jsdocType, decl.body);
121120
default:
122121
Debug.fail(`Unexpected SyntaxKind: ${decl.kind}`);
123122
return undefined;
@@ -144,4 +143,82 @@ namespace ts.refactor.annotateWithTypeFromJSDoc {
144143
&& !parameter.initializer // parameter may not have an initializer
145144
&& isIdentifier(parameter.name); // parameter name must be identifier
146145
}
146+
147+
function transformJSDocType(node: Node): Node | undefined {
148+
if (node === undefined) {
149+
return undefined;
150+
}
151+
switch (node.kind) {
152+
case SyntaxKind.JSDocAllType:
153+
case SyntaxKind.JSDocUnknownType:
154+
return createTypeReferenceNode("any", emptyArray);
155+
case SyntaxKind.JSDocOptionalType:
156+
return visitJSDocOptionalType(node as JSDocOptionalType);
157+
case SyntaxKind.JSDocNonNullableType:
158+
return transformJSDocType((node as JSDocNonNullableType).type);
159+
case SyntaxKind.JSDocNullableType:
160+
return visitJSDocNullableType(node as JSDocNullableType);
161+
case SyntaxKind.JSDocVariadicType:
162+
return visitJSDocVariadicType(node as JSDocVariadicType);
163+
case SyntaxKind.JSDocFunctionType:
164+
return visitJSDocFunctionType(node as JSDocFunctionType);
165+
case SyntaxKind.Parameter:
166+
return visitJSDocParameter(node as ParameterDeclaration);
167+
case SyntaxKind.TypeReference:
168+
return visitJSDocTypeReference(node as TypeReferenceNode);
169+
default:
170+
return visitEachChild(node, transformJSDocType, /*context*/ undefined) as TypeNode;
171+
}
172+
}
173+
174+
function visitJSDocOptionalType(node: JSDocOptionalType) {
175+
return createUnionTypeNode([visitNode(node.type, transformJSDocType), createTypeReferenceNode("undefined", emptyArray)]);
176+
}
177+
178+
function visitJSDocNullableType(node: JSDocNullableType) {
179+
return createUnionTypeNode([visitNode(node.type, transformJSDocType), createTypeReferenceNode("null", emptyArray)]);
180+
}
181+
182+
function visitJSDocVariadicType(node: JSDocVariadicType) {
183+
return createArrayTypeNode(visitNode(node.type, transformJSDocType));
184+
}
185+
186+
function visitJSDocFunctionType(node: JSDocFunctionType) {
187+
const parameters = node.parameters && node.parameters.map(transformJSDocType);
188+
return createFunctionTypeNode(emptyArray, parameters as ParameterDeclaration[], node.type);
189+
}
190+
191+
function visitJSDocParameter(node: ParameterDeclaration) {
192+
const name = node.name || "arg" + node.parent.parameters.indexOf(node);
193+
return createParameter(node.decorators, node.modifiers, node.dotDotDotToken, name, node.questionToken, node.type, node.initializer);
194+
}
195+
196+
function visitJSDocTypeReference(node: TypeReferenceNode) {
197+
let name = node.typeName;
198+
let args = node.typeArguments;
199+
if (isIdentifier(node.typeName)) {
200+
let text = node.typeName.text;
201+
switch (node.typeName.text) {
202+
case "String":
203+
case "Boolean":
204+
case "Object":
205+
case "Number":
206+
text = text.toLowerCase();
207+
break;
208+
case "array":
209+
case "date":
210+
case "promise":
211+
text = text[0].toUpperCase() + text.slice(1);
212+
break;
213+
}
214+
name = createIdentifier(text);
215+
if ((text === "Array" || text === "Promise") && !node.typeArguments) {
216+
args = createNodeArray([createTypeReferenceNode("any", emptyArray)]);
217+
}
218+
else {
219+
args = visitNodes(node.typeArguments, transformJSDocType);
220+
}
221+
}
222+
return createTypeReferenceNode(name, args);
223+
}
147224
}

0 commit comments

Comments
 (0)