Codefix for ?! pre/postfix JSDoc types

For ?, provide two code fixes, one for jsdoc/closure semantics
(`?t -> t | null)` and one for flow semantics
(`?t -> t | null | undefined`).

The current way of doing this is the hackiest thing you can imagine, but
it was easier than lifting everything into the list monad for a code fix
that I might not actually keep.
This commit is contained in:
Nathan Shively-Sanders 2017-07-17 11:06:20 -07:00
parent 3776b0b58b
commit f9e5576d58
4 changed files with 58 additions and 23 deletions

View file

@ -11,41 +11,64 @@ namespace ts.codefix {
const decl = ts.findAncestor(node, n => n.kind === SyntaxKind.VariableDeclaration);
if (!decl) return;
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));
return [{
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(),
}];
}
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.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;
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(),
});
}
// TODO: Need to recur on all relevant nodes. Is a call to visit enough?
return type;
}
return changes;
function getTypeReferenceFromJSDocType(type: TypeReferenceNode) {
if (type.typeArguments && type.typeName.jsdocDotPos) {
return createTypeReferenceNode(type.typeName, map(type.typeArguments, getTypeFromJSDocType));
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);
}
return getTypeFromJSDocType(type);
}
}

View file

@ -0,0 +1,4 @@
/// <reference path='fourslash.ts' />
//// var x: [|?number|] = 12;
verify.rangeAfterCodeFix("number | null", /*includeWhiteSpace*/ false, /*errorCode*/ 8020, 0);

View file

@ -0,0 +1,4 @@
/// <reference path='fourslash.ts' />
//// var x: [|number?|] = 12;
verify.rangeAfterCodeFix("number | null | undefined", /*includeWhiteSpace*/ undefined, /*errorCode*/ undefined, 1);

View file

@ -0,0 +1,4 @@
/// <reference path='fourslash.ts' />
//// var x: [|!number|] = 12;
verify.rangeAfterCodeFix("number");