Skip to content

Commit d6f5fc6

Browse files
committed
add Initializer if enum value is another enum
1 parent f426e04 commit d6f5fc6

5 files changed

Lines changed: 133 additions & 48 deletions

File tree

src/services/codefixes/fixAddMissingMember.ts

Lines changed: 30 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,16 @@ namespace ts.codefix {
1212
const info = getInfo(context.sourceFile, context.span.start, context.program.getTypeChecker());
1313
if (!info) return undefined;
1414

15-
if (isEnumInfo(info)) {
16-
return singleElementArray(getActionForEnumMemberDeclaration(context, info.enumDeclarationSourceFile, info.declaration, info.token));
15+
if (info.kind === InfoKind.enum) {
16+
const { token, enumDeclaration } = info;
17+
const changes = textChanges.ChangeTracker.with(context, t => addEnumMemberDeclaration(t, context.program.getTypeChecker(), token, enumDeclaration));
18+
return singleElementArray(createCodeFixAction(fixName, changes, [Diagnostics.Add_missing_enum_member_0, token.text], fixId, Diagnostics.Add_all_missing_enum_members));
1719
}
18-
const { declaration, classDeclarationSourceFile, inJs, makeStatic, token, call } = info;
19-
const methodCodeAction = call && getActionForMethodDeclaration(context, classDeclarationSourceFile, declaration, token, call, makeStatic, inJs, context.preferences);
20+
const { classDeclaration, classDeclarationSourceFile, inJs, makeStatic, token, call } = info;
21+
const methodCodeAction = call && getActionForMethodDeclaration(context, classDeclarationSourceFile, classDeclaration, token, call, makeStatic, inJs, context.preferences);
2022
const addMember = inJs ?
21-
singleElementArray(getActionsForAddMissingMemberInJavaScriptFile(context, classDeclarationSourceFile, declaration, token.text, makeStatic)) :
22-
getActionsForAddMissingMemberInTypeScriptFile(context, classDeclarationSourceFile, declaration, token, makeStatic);
23+
singleElementArray(getActionsForAddMissingMemberInJavaScriptFile(context, classDeclarationSourceFile, classDeclaration, token.text, makeStatic)) :
24+
getActionsForAddMissingMemberInTypeScriptFile(context, classDeclarationSourceFile, classDeclaration, token, makeStatic);
2325
return concatenate(singleElementArray(methodCodeAction), addMember);
2426
},
2527
fixIds: [fixId],
@@ -33,38 +35,35 @@ namespace ts.codefix {
3335
return;
3436
}
3537

36-
if (isEnumInfo(info)) {
37-
const { token, declaration, enumDeclarationSourceFile } = info;
38-
addEnumMemberDeclaration(changes, checker, token, declaration, enumDeclarationSourceFile);
38+
if (info.kind === InfoKind.enum) {
39+
const { token, enumDeclaration } = info;
40+
addEnumMemberDeclaration(changes, checker, token, enumDeclaration);
3941
}
4042
else {
41-
const { declaration, classDeclarationSourceFile, inJs, makeStatic, token, call } = info;
43+
const { classDeclaration, classDeclarationSourceFile, inJs, makeStatic, token, call } = info;
4244
// Always prefer to add a method declaration if possible.
4345
if (call) {
44-
addMethodDeclaration(context, changes, classDeclarationSourceFile, declaration, token, call, makeStatic, inJs, preferences);
46+
addMethodDeclaration(context, changes, classDeclarationSourceFile, classDeclaration, token, call, makeStatic, inJs, preferences);
4547
}
4648
else {
4749
if (inJs) {
48-
addMissingMemberInJs(changes, classDeclarationSourceFile, declaration, token.text, makeStatic);
50+
addMissingMemberInJs(changes, classDeclarationSourceFile, classDeclaration, token.text, makeStatic);
4951
}
5052
else {
51-
const typeNode = getTypeNode(program.getTypeChecker(), declaration, token);
52-
addPropertyDeclaration(changes, classDeclarationSourceFile, declaration, token.text, typeNode, makeStatic);
53+
const typeNode = getTypeNode(program.getTypeChecker(), classDeclaration, token);
54+
addPropertyDeclaration(changes, classDeclarationSourceFile, classDeclaration, token.text, typeNode, makeStatic);
5355
}
5456
}
5557
}
5658
});
5759
},
5860
});
5961

60-
interface EnumInfo { token: Identifier; declaration: EnumDeclaration; enumDeclarationSourceFile: SourceFile; }
61-
interface ClassInfo { token: Identifier; declaration: ClassLikeDeclaration; makeStatic: boolean; classDeclarationSourceFile: SourceFile; inJs: boolean; call: CallExpression | undefined; }
62+
enum InfoKind { enum, class }
63+
interface EnumInfo { kind: InfoKind.enum; token: Identifier; enumDeclaration: EnumDeclaration; }
64+
interface ClassInfo { kind: InfoKind.class; token: Identifier; classDeclaration: ClassLikeDeclaration; makeStatic: boolean; classDeclarationSourceFile: SourceFile; inJs: boolean; call: CallExpression | undefined; }
6265
type Info = EnumInfo | ClassInfo;
6366

64-
function isEnumInfo (info: Info): info is EnumInfo {
65-
return isEnumDeclaration(info.declaration);
66-
}
67-
6867
function getInfo(tokenSourceFile: SourceFile, tokenPos: number, checker: TypeChecker): Info | undefined {
6968
// The identifier of the missing property. eg:
7069
// this.missing = 1;
@@ -87,11 +86,11 @@ namespace ts.codefix {
8786
const classDeclarationSourceFile = classDeclaration.getSourceFile();
8887
const inJs = isSourceFileJavaScript(classDeclarationSourceFile);
8988
const call = tryCast(parent.parent, isCallExpression);
90-
return { token, declaration: classDeclaration, makeStatic, classDeclarationSourceFile, inJs, call };
89+
return { kind: InfoKind.class, token, classDeclaration, makeStatic, classDeclarationSourceFile, inJs, call };
9190
}
9291
const enumDeclaration = find(symbol.declarations, isEnumDeclaration);
9392
if (enumDeclaration) {
94-
return { token, declaration: enumDeclaration, enumDeclarationSourceFile: enumDeclaration.getSourceFile() };
93+
return { kind: InfoKind.enum, token, enumDeclaration };
9594
}
9695
return undefined;
9796
}
@@ -211,16 +210,6 @@ namespace ts.codefix {
211210
return createCodeFixAction(fixName, changes, [makeStatic ? Diagnostics.Declare_static_method_0 : Diagnostics.Declare_method_0, token.text], fixId, Diagnostics.Add_all_missing_members);
212211
}
213212

214-
function getActionForEnumMemberDeclaration(
215-
context: CodeFixContext,
216-
enumDeclarationSourceFile: SourceFile,
217-
enumDeclaration: EnumDeclaration,
218-
token: Identifier
219-
): CodeFixAction | undefined {
220-
const changes = textChanges.ChangeTracker.with(context, t => addEnumMemberDeclaration(t, context.program.getTypeChecker(), token, enumDeclaration, enumDeclarationSourceFile));
221-
return createCodeFixAction(fixName, changes, [Diagnostics.Add_missing_enum_member_0, token.text], fixId, Diagnostics.Add_all_missing_enum_members);
222-
}
223-
224213
function addMethodDeclaration(
225214
context: CodeFixContextBase,
226215
changeTracker: textChanges.ChangeTracker,
@@ -243,26 +232,19 @@ namespace ts.codefix {
243232
}
244233
}
245234

246-
function createEnumMemberFromEnumDeclaration(checker: TypeChecker, token: Identifier, enumDeclaration: EnumDeclaration) {
235+
function addEnumMemberDeclaration(changes: textChanges.ChangeTracker, checker: TypeChecker, token: Identifier, enumDeclaration: EnumDeclaration) {
247236
/**
248-
* create initializer only string enum.
237+
* create initializer only literal enum that has string initializer.
249238
* value of initializer is a string literal that equal to name of enum member.
250-
* literal enum or empty enum will not create initializer.
239+
* numeric enum or empty enum will not create initializer.
251240
*/
252-
const firstMember = firstOrUndefined(enumDeclaration.members);
253-
let enumMemberInitializer: Expression | undefined;
254-
if (firstMember && firstMember.initializer) {
255-
const memberType = checker.getTypeAtLocation(firstMember.initializer);
256-
if (memberType && memberType.flags & TypeFlags.StringLike) {
257-
enumMemberInitializer = createStringLiteral(token.text);
258-
}
259-
}
260-
return createEnumMember(token, enumMemberInitializer);
261-
}
241+
const hasStringInitializer = some(enumDeclaration.members, member => {
242+
const type = checker.getTypeAtLocation(member);
243+
return !!(type && type.flags & (TypeFlags.StringLike | TypeFlags.Enum));
244+
});
262245

263-
function addEnumMemberDeclaration(changes: textChanges.ChangeTracker, checker: TypeChecker, token: Identifier, enumDeclaration: EnumDeclaration, file: SourceFile) {
264-
const enumMember = createEnumMemberFromEnumDeclaration(checker, token, enumDeclaration);
265-
changes.replaceNode(file, enumDeclaration, updateEnumDeclaration(
246+
const enumMember = createEnumMember(token, hasStringInitializer ? createStringLiteral(token.text) : undefined);
247+
changes.replaceNode(enumDeclaration.getSourceFile(), enumDeclaration, updateEnumDeclaration(
266248
enumDeclaration,
267249
enumDeclaration.decorators,
268250
enumDeclaration.modifiers,
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/// <reference path='fourslash.ts' />
2+
3+
////enum E {
4+
//// a,
5+
//// b = 1,
6+
//// c = "123"
7+
////}
8+
////enum A {
9+
//// a = E.c
10+
////}
11+
////A.b
12+
13+
verify.codeFix({
14+
description: "Add missing enum member 'b'",
15+
newFileContent: `enum E {
16+
a,
17+
b = 1,
18+
c = "123"
19+
}
20+
enum A {
21+
a = E.c,
22+
b = "b"
23+
}
24+
A.b`
25+
});
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/// <reference path='fourslash.ts' />
2+
3+
////enum E {
4+
//// a,
5+
//// b = 1,
6+
//// c = "123"
7+
////}
8+
////enum A {
9+
//// a = E.c
10+
////}
11+
////enum B {
12+
//// b = A.a
13+
////}
14+
////B.c
15+
16+
verify.codeFix({
17+
description: "Add missing enum member 'c'",
18+
newFileContent: `enum E {
19+
a,
20+
b = 1,
21+
c = "123"
22+
}
23+
enum A {
24+
a = E.c
25+
}
26+
enum B {
27+
b = A.a,
28+
c = "c"
29+
}
30+
B.c`
31+
});
32+
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
/// <reference path='fourslash.ts' />
2+
3+
////enum E {
4+
//// a,
5+
//// b = 1,
6+
//// c = "123"
7+
////}
8+
////E.d
9+
10+
verify.codeFix({
11+
description: "Add missing enum member 'd'",
12+
newFileContent: `enum E {
13+
a,
14+
b = 1,
15+
c = "123",
16+
d = "d"
17+
}
18+
E.d`
19+
});
20+
21+
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/// <reference path='fourslash.ts' />
2+
3+
////enum E {
4+
//// a,
5+
//// b = 1,
6+
//// c = "123"
7+
////}
8+
////enum A {
9+
//// a = E.a
10+
////}
11+
////A.b
12+
13+
verify.codeFix({
14+
description: "Add missing enum member 'b'",
15+
newFileContent: `enum E {
16+
a,
17+
b = 1,
18+
c = "123"
19+
}
20+
enum A {
21+
a = E.a,
22+
b = "b"
23+
}
24+
A.b`
25+
});

0 commit comments

Comments
 (0)