Skip to content

Commit

Permalink
feat(25770): add Quick Fix to convert LiteralType to MappedType (#40226)
Browse files Browse the repository at this point in the history
  • Loading branch information
a-tarasyuk authored Sep 8, 2020
1 parent 6101fbc commit 6aec7f4
Show file tree
Hide file tree
Showing 6 changed files with 118 additions and 0 deletions.
8 changes: 8 additions & 0 deletions src/compiler/diagnosticMessages.json
Original file line number Diff line number Diff line change
Expand Up @@ -5335,6 +5335,10 @@
"category": "Message",
"code": 95002
},
"Convert '{0}' to '{1} in {0}'": {
"category": "Message",
"code": 95003
},
"Extract to {0} in {1}": {
"category": "Message",
"code": 95004
Expand Down Expand Up @@ -5403,6 +5407,10 @@
"category": "Message",
"code": 95020
},
"Convert all type literals to mapped type": {
"category": "Message",
"code": 95021
},
"Add all missing members": {
"category": "Message",
"code": 95022
Expand Down
53 changes: 53 additions & 0 deletions src/services/codefixes/convertLiteralTypeToMappedType.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/* @internal */
namespace ts.codefix {
const fixId = "convertLiteralTypeToMappedType";
const errorCodes = [Diagnostics._0_only_refers_to_a_type_but_is_being_used_as_a_value_here_Did_you_mean_to_use_1_in_0.code];

registerCodeFix({
errorCodes,
getCodeActions: context => {
const { sourceFile, span } = context;
const info = getInfo(sourceFile, span.start);
if (!info) {
return undefined;
}
const { name, constraint } = info;
const changes = textChanges.ChangeTracker.with(context, t => doChange(t, sourceFile, info));
return [createCodeFixAction(fixId, changes, [Diagnostics.Convert_0_to_1_in_0, constraint, name], fixId, Diagnostics.Convert_all_type_literals_to_mapped_type)];
},
fixIds: [fixId],
getAllCodeActions: context => codeFixAll(context, errorCodes, (changes, diag) => {
const info = getInfo(diag.file, diag.start);
if (info) {
doChange(changes, diag.file, info);
}
})
});

interface Info {
container: TypeLiteralNode,
typeNode: TypeNode | undefined;
constraint: string;
name: string;
}

function getInfo(sourceFile: SourceFile, pos: number): Info | undefined {
const token = getTokenAtPosition(sourceFile, pos);
if (isIdentifier(token)) {
const propertySignature = cast(token.parent.parent, isPropertySignature);
const propertyName = token.getText(sourceFile);
return {
container: cast(propertySignature.parent, isTypeLiteralNode),
typeNode: propertySignature.type,
constraint: propertyName,
name: propertyName === "K" ? "P" : "K",
};
}
return undefined;
}

function doChange(changes: textChanges.ChangeTracker, sourceFile: SourceFile, { container, typeNode, constraint, name }: Info): void {
changes.replaceNode(sourceFile, container, factory.createMappedTypeNode(/*readonlyToken*/ undefined,
factory.createTypeParameterDeclaration(name, factory.createTypeReferenceNode(constraint)), /*questionToken*/ undefined, typeNode));
}
}
1 change: 1 addition & 0 deletions src/services/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@
"codefixes/correctQualifiedNameToIndexedAccessType.ts",
"codefixes/convertToTypeOnlyExport.ts",
"codefixes/convertToTypeOnlyImport.ts",
"codefixes/convertLiteralTypeToMappedType.ts",
"codefixes/fixClassIncorrectlyImplementsInterface.ts",
"codefixes/importFixes.ts",
"codefixes/fixImplicitThis.ts",
Expand Down
16 changes: 16 additions & 0 deletions tests/cases/fourslash/convertLiteralTypeToMappedType1.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/// <reference path='fourslash.ts' />

////type K = number | string;
////type T = {
//// [K]: number;
////}

verify.codeFix({
description: [ts.Diagnostics.Convert_0_to_1_in_0.message, "K", "P"],
index: 0,
newFileContent:
`type K = number | string;
type T = {
[P in K]: number;
}`
});
16 changes: 16 additions & 0 deletions tests/cases/fourslash/convertLiteralTypeToMappedType2.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/// <reference path='fourslash.ts' />

////type Keys = number | string;
////type T = {
//// [Keys]: number;
////}

verify.codeFix({
description: [ts.Diagnostics.Convert_0_to_1_in_0.message, "Keys", "K"],
index: 0,
newFileContent:
`type Keys = number | string;
type T = {
[K in Keys]: number;
}`
});
24 changes: 24 additions & 0 deletions tests/cases/fourslash/convertLiteralTypeToMappedType3.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/// <reference path='fourslash.ts' />

////type K1 = number | string;
////type T1 = {
//// [K1]: number;
////}
////type K2 = number | string;
////type T2 = {
//// [K2]: number;
////}

verify.codeFixAll({
fixAllDescription: ts.Diagnostics.Convert_all_type_literals_to_mapped_type.message,
fixId: 'convertLiteralTypeToMappedType',
newFileContent:
`type K1 = number | string;
type T1 = {
[K in K1]: number;
}
type K2 = number | string;
type T2 = {
[K in K2]: number;
}`
});

0 comments on commit 6aec7f4

Please sign in to comment.