mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-15 03:23:08 -06:00
JSDoc codefix:getTypeFromTypeNode >>> typeToString
Instead of trying to walk the type structure in the codefix, I changed to call getTypeFromTypeNode in the checker and then calling typeToString. Together, these two functions normalise out JSDoc. Note that you only get `T | null` for `?T` if you have --strict on. This is technically correct -- adding a null union does nothing without strict -- but it would still serve as documentation.
This commit is contained in:
parent
cbe7b4dba3
commit
b13de0547e
@ -117,6 +117,7 @@ namespace ts {
|
||||
},
|
||||
getParameterType: getTypeAtPosition,
|
||||
getReturnTypeOfSignature,
|
||||
getNullableType,
|
||||
getNonNullableType,
|
||||
typeToTypeNode: nodeBuilder.typeToTypeNode,
|
||||
indexInfoToIndexSignatureDeclaration: nodeBuilder.indexInfoToIndexSignatureDeclaration,
|
||||
|
||||
@ -2520,6 +2520,7 @@ namespace ts {
|
||||
* Returns `any` if the index is not valid.
|
||||
*/
|
||||
/* @internal */ getParameterType(signature: Signature, parameterIndex: number): Type;
|
||||
getNullableType(type: Type, flags: TypeFlags): Type;
|
||||
getNonNullableType(type: Type): Type;
|
||||
|
||||
/** Note that the resulting nodes cannot be checked. */
|
||||
|
||||
@ -10,65 +10,31 @@ namespace ts.codefix {
|
||||
const node = getTokenAtPosition(sourceFile, context.span.start, /*includeJsDocComment*/ false);
|
||||
const decl = ts.findAncestor(node, n => n.kind === SyntaxKind.VariableDeclaration);
|
||||
if (!decl) return;
|
||||
const checker = context.program.getTypeChecker();
|
||||
|
||||
const jsdocType = (decl as VariableDeclaration).type;
|
||||
let cheesyHacks = false;
|
||||
|
||||
// TODO: Only if get(jsdoctype) !== jsdoctype
|
||||
// TODO: Create cheesy hacks to support | null | undefined -- just flip a boolean and rerun with that boolean set
|
||||
|
||||
const trk = textChanges.ChangeTracker.fromCodeFixContext(context);
|
||||
trk.replaceNode(sourceFile, jsdocType, getTypeFromJSDocType(jsdocType));
|
||||
const changes = [{
|
||||
// TODO: This seems like the LEAST SAFE way to get the new text
|
||||
description: formatStringFromArgs(getLocaleSpecificMessage(Diagnostics.Change_0_to_1), [getTextOfNode(jsdocType), trk.getChanges()[0].textChanges[0].newText]),
|
||||
changes: trk.getChanges(),
|
||||
}];
|
||||
|
||||
if (cheesyHacks) {
|
||||
const trk = textChanges.ChangeTracker.fromCodeFixContext(context);
|
||||
trk.replaceNode(sourceFile, jsdocType, getTypeFromJSDocType(jsdocType));
|
||||
changes.push({
|
||||
// TODO: This seems like the LEAST SAFE way to get the new text
|
||||
description: formatStringFromArgs(getLocaleSpecificMessage(Diagnostics.Change_0_to_1), [getTextOfNode(jsdocType), trk.getChanges()[0].textChanges[0].newText]),
|
||||
changes: trk.getChanges(),
|
||||
});
|
||||
const original = getTextOfNode(jsdocType);
|
||||
const type = checker.getTypeFromTypeNode(jsdocType);
|
||||
const actions = [createAction(jsdocType, sourceFile.fileName, original, checker.typeToString(type))];
|
||||
if (jsdocType.kind === SyntaxKind.JSDocNullableType) {
|
||||
// for nullable types, suggest the flow-compatible `T | null | undefined`
|
||||
// in addition to the jsdoc/closure-compatible `T | null`
|
||||
const replacementWithUndefined = checker.typeToString(checker.getNullableType(type, TypeFlags.Undefined));
|
||||
actions.push(createAction(jsdocType, sourceFile.fileName, original, replacementWithUndefined));
|
||||
}
|
||||
return changes;
|
||||
return actions;
|
||||
}
|
||||
|
||||
function getTypeFromJSDocType(type: TypeNode): TypeNode {
|
||||
switch (type.kind) {
|
||||
case SyntaxKind.JSDocUnknownType:
|
||||
case SyntaxKind.JSDocAllType:
|
||||
return createToken(SyntaxKind.AnyKeyword) as TypeNode;
|
||||
case SyntaxKind.JSDocVariadicType:
|
||||
return createArrayTypeNode(getTypeFromJSDocType((type as JSDocVariadicType).type));
|
||||
case SyntaxKind.JSDocNullableType:
|
||||
if (cheesyHacks) {
|
||||
return createUnionTypeNode([getTypeFromJSDocType((type as JSDocNullableType).type), createNull(), createToken(SyntaxKind.UndefinedKeyword) as TypeNode]);
|
||||
}
|
||||
else {
|
||||
cheesyHacks = true;
|
||||
return createUnionTypeNode([getTypeFromJSDocType((type as JSDocNullableType).type), createNull()]);
|
||||
}
|
||||
case SyntaxKind.JSDocNonNullableType:
|
||||
return getTypeFromJSDocType((type as JSDocNullableType).type);
|
||||
case SyntaxKind.ArrayType:
|
||||
// TODO: Only create an error if the get(type.type) !== type.type.
|
||||
return createArrayTypeNode(getTypeFromJSDocType((type as ArrayTypeNode).elementType));
|
||||
case SyntaxKind.TypeReference:
|
||||
return getTypeReferenceFromJSDocType(type as TypeReferenceNode);
|
||||
case SyntaxKind.Identifier:
|
||||
return type;
|
||||
}
|
||||
// TODO: Need to recur on all relevant nodes. Is a call to visit enough?
|
||||
return type;
|
||||
}
|
||||
|
||||
function getTypeReferenceFromJSDocType(type: TypeReferenceNode) {
|
||||
if (type.typeArguments && type.typeName.jsdocDotPos) {
|
||||
return createTypeReferenceNode(type.typeName, map(type.typeArguments, getTypeFromJSDocType));
|
||||
}
|
||||
return getTypeFromJSDocType(type);
|
||||
}
|
||||
function createAction(declaration: TypeNode, fileName: string, original: string, replacement: string): CodeAction {
|
||||
return {
|
||||
description: formatStringFromArgs(getLocaleSpecificMessage(Diagnostics.Change_0_to_1), [original, replacement]),
|
||||
changes: [{
|
||||
fileName,
|
||||
textChanges: [{
|
||||
span: { start: declaration.getStart(), length: declaration.getWidth() },
|
||||
newText: replacement
|
||||
}]
|
||||
}],
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user