From 839e72ea210d6bf3a79aef00779e12ffdc5e721b Mon Sep 17 00:00:00 2001 From: Oleksandr T Date: Sun, 13 Jun 2021 23:28:15 +0300 Subject: [PATCH] feat(28491): add QF to declare missing properties --- src/compiler/checker.ts | 1 + src/compiler/diagnosticMessages.json | 8 ++ src/compiler/types.ts | 1 + src/services/codefixes/fixAddMissingMember.ts | 127 +++++++++++++++++- src/services/codefixes/helpers.ts | 43 +++--- .../fourslash/codeFixAddMissingProperties1.ts | 39 ++++++ .../codeFixAddMissingProperties10.ts | 18 +++ .../codeFixAddMissingProperties11.ts | 22 +++ .../fourslash/codeFixAddMissingProperties2.ts | 23 ++++ .../fourslash/codeFixAddMissingProperties3.ts | 19 +++ .../fourslash/codeFixAddMissingProperties4.ts | 17 +++ .../fourslash/codeFixAddMissingProperties5.ts | 16 +++ .../fourslash/codeFixAddMissingProperties6.ts | 23 ++++ .../fourslash/codeFixAddMissingProperties7.ts | 27 ++++ .../fourslash/codeFixAddMissingProperties8.ts | 29 ++++ .../fourslash/codeFixAddMissingProperties9.ts | 23 ++++ .../codeFixAddMissingProperties_all.ts | 78 +++++++++++ 17 files changed, 488 insertions(+), 26 deletions(-) create mode 100644 tests/cases/fourslash/codeFixAddMissingProperties1.ts create mode 100644 tests/cases/fourslash/codeFixAddMissingProperties10.ts create mode 100644 tests/cases/fourslash/codeFixAddMissingProperties11.ts create mode 100644 tests/cases/fourslash/codeFixAddMissingProperties2.ts create mode 100644 tests/cases/fourslash/codeFixAddMissingProperties3.ts create mode 100644 tests/cases/fourslash/codeFixAddMissingProperties4.ts create mode 100644 tests/cases/fourslash/codeFixAddMissingProperties5.ts create mode 100644 tests/cases/fourslash/codeFixAddMissingProperties6.ts create mode 100644 tests/cases/fourslash/codeFixAddMissingProperties7.ts create mode 100644 tests/cases/fourslash/codeFixAddMissingProperties8.ts create mode 100644 tests/cases/fourslash/codeFixAddMissingProperties9.ts create mode 100644 tests/cases/fourslash/codeFixAddMissingProperties_all.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 44cd5f0608345..9e55b509c81ef 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -393,6 +393,7 @@ namespace ts { getDiagnostics, getGlobalDiagnostics, getRecursionIdentity, + getUnmatchedProperties, getTypeOfSymbolAtLocation: (symbol, locationIn) => { const location = getParseTreeNode(locationIn); return location ? getTypeOfSymbolAtLocation(symbol, location) : errorType; diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index d2ddbc8b0a4ec..49881178a8bc1 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -6496,6 +6496,14 @@ "category": "Message", "code": 95164 }, + "Add missing properties": { + "category": "Message", + "code": 95165 + }, + "Add all missing properties": { + "category": "Message", + "code": 95166 + }, "No value exists in scope for the shorthand property '{0}'. Either declare one or provide an initializer.": { "category": "Error", diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 9cc2da184dec0..f68fed47e548a 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -4272,6 +4272,7 @@ namespace ts { /* @internal */ getInstantiationCount(): number; /* @internal */ getRelationCacheSizes(): { assignable: number, identity: number, subtype: number, strictSubtype: number }; /* @internal */ getRecursionIdentity(type: Type): object | undefined; + /* @internal */ getUnmatchedProperties(source: Type, target: Type, requireOptionalProperties: boolean, matchDiscriminantProperties: boolean): IterableIterator; /* @internal */ isArrayType(type: Type): boolean; /* @internal */ isTupleType(type: Type): boolean; diff --git a/src/services/codefixes/fixAddMissingMember.ts b/src/services/codefixes/fixAddMissingMember.ts index 92a6f7647a1fc..65af98b64f5ec 100644 --- a/src/services/codefixes/fixAddMissingMember.ts +++ b/src/services/codefixes/fixAddMissingMember.ts @@ -1,7 +1,9 @@ /* @internal */ namespace ts.codefix { const fixMissingMember = "fixMissingMember"; + const fixMissingProperties = "fixMissingProperties"; const fixMissingFunctionDeclaration = "fixMissingFunctionDeclaration"; + const errorCodes = [ Diagnostics.Property_0_does_not_exist_on_type_1.code, Diagnostics.Property_0_does_not_exist_on_type_1_Did_you_mean_2.code, @@ -19,6 +21,10 @@ namespace ts.codefix { if (!info) { return undefined; } + if (info.kind === InfoKind.ObjectLiteral) { + const changes = textChanges.ChangeTracker.with(context, t => addObjectLiteralProperties(t, context, info)); + return [createCodeFixAction(fixMissingProperties, changes, Diagnostics.Add_missing_properties, fixMissingProperties, Diagnostics.Add_all_missing_properties)]; + } if (info.kind === InfoKind.Function) { const changes = textChanges.ChangeTracker.with(context, t => addFunctionDeclaration(t, context, info)); return [createCodeFixAction(fixMissingFunctionDeclaration, changes, [Diagnostics.Add_missing_function_declaration_0, info.token.text], fixMissingFunctionDeclaration, Diagnostics.Add_all_missing_function_declarations)]; @@ -29,7 +35,7 @@ namespace ts.codefix { } return concatenate(getActionsForMissingMethodDeclaration(context, info), getActionsForMissingMemberDeclaration(context, info)); }, - fixIds: [fixMissingMember, fixMissingFunctionDeclaration], + fixIds: [fixMissingMember, fixMissingFunctionDeclaration, fixMissingProperties], getAllCodeActions: context => { const { program, fixId } = context; const checker = program.getTypeChecker(); @@ -48,11 +54,15 @@ namespace ts.codefix { addFunctionDeclaration(changes, context, info); } } + else if (fixId === fixMissingProperties) { + if (info.kind === InfoKind.ObjectLiteral) { + addObjectLiteralProperties(changes, context, info); + } + } else { if (info.kind === InfoKind.Enum) { addEnumMemberDeclaration(changes, checker, info); } - if (info.kind === InfoKind.ClassOrInterface) { const { parentDeclaration, token } = info; const infos = getOrUpdate(typeDeclToMembers, parentDeclaration, () => []); @@ -92,8 +102,8 @@ namespace ts.codefix { }, }); - const enum InfoKind { Enum, ClassOrInterface, Function } - type Info = EnumInfo | ClassOrInterfaceInfo | FunctionInfo; + const enum InfoKind { Enum, ClassOrInterface, Function, ObjectLiteral } + type Info = EnumInfo | ClassOrInterfaceInfo | FunctionInfo | ObjectLiteralInfo; interface EnumInfo { readonly kind: InfoKind.Enum; @@ -120,6 +130,13 @@ namespace ts.codefix { readonly parentDeclaration: SourceFile | ModuleDeclaration; } + interface ObjectLiteralInfo { + readonly kind: InfoKind.ObjectLiteral; + readonly token: Identifier; + readonly properties: Symbol[]; + readonly parentDeclaration: ObjectLiteralExpression; + } + function getInfo(sourceFile: SourceFile, tokenPos: number, checker: TypeChecker, program: Program): Info | undefined { // The identifier of the missing property. eg: // this.missing = 1; @@ -130,6 +147,13 @@ namespace ts.codefix { } const { parent } = token; + if (isIdentifier(token) && hasInitializer(parent) && parent.initializer && isObjectLiteralExpression(parent.initializer)) { + const properties = arrayFrom(checker.getUnmatchedProperties(checker.getTypeAtLocation(parent.initializer), checker.getTypeAtLocation(token), /* requireOptionalProperties */ false, /* matchDiscriminantProperties */ false)); + if (length(properties)) { + return { kind: InfoKind.ObjectLiteral, token, properties, parentDeclaration: parent.initializer }; + } + } + if (isIdentifier(token) && isCallExpression(parent)) { return { kind: InfoKind.Function, token, call: parent, sourceFile, modifierFlags: ModifierFlags.None, parentDeclaration: sourceFile }; } @@ -248,7 +272,7 @@ namespace ts.codefix { } function initializePropertyToUndefined(obj: Expression, propertyName: string) { - return factory.createExpressionStatement(factory.createAssignment(factory.createPropertyAccessExpression(obj, propertyName), factory.createIdentifier("undefined"))); + return factory.createExpressionStatement(factory.createAssignment(factory.createPropertyAccessExpression(obj, propertyName), createUndefined())); } function createActionsForAddMissingMemberInTypeScriptFile(context: CodeFixContext, { parentDeclaration, declSourceFile, modifierFlags, token }: ClassOrInterfaceInfo): CodeFixAction[] | undefined { @@ -405,4 +429,97 @@ namespace ts.codefix { const functionDeclaration = createSignatureDeclarationFromCallExpression(SyntaxKind.FunctionDeclaration, context, importAdder, info.call, idText(info.token), info.modifierFlags, info.parentDeclaration) as FunctionDeclaration; changes.insertNodeAtEndOfScope(info.sourceFile, info.parentDeclaration, functionDeclaration); } + + function addObjectLiteralProperties(changes: textChanges.ChangeTracker, context: CodeFixContextBase, info: ObjectLiteralInfo) { + const importAdder = createImportAdder(context.sourceFile, context.program, context.preferences, context.host); + const quotePreference = getQuotePreference(context.sourceFile, context.preferences); + const checker = context.program.getTypeChecker(); + const props = map(info.properties, prop => { + const initializer = prop.valueDeclaration ? tryGetInitializerValueFromType(context, checker, importAdder, quotePreference, checker.getTypeAtLocation(prop.valueDeclaration)) : createUndefined(); + return factory.createPropertyAssignment(prop.name, initializer); + }); + changes.replaceNode(context.sourceFile, info.parentDeclaration, factory.createObjectLiteralExpression([...info.parentDeclaration.properties, ...props], /*multiLine*/ true)); + } + + function tryGetInitializerValueFromType(context: CodeFixContextBase, checker: TypeChecker, importAdder: ImportAdder, quotePreference: QuotePreference, type: Type): Expression { + if (type.flags & TypeFlags.AnyOrUnknown) { + return createUndefined(); + } + if (type.flags & (TypeFlags.String | TypeFlags.TemplateLiteral)) { + return factory.createStringLiteral("", /* isSingleQuote */ quotePreference === QuotePreference.Single); + } + if (type.flags & TypeFlags.Number) { + return factory.createNumericLiteral(0); + } + if (type.flags & TypeFlags.BigInt) { + return factory.createBigIntLiteral("0n"); + } + if (type.flags & TypeFlags.Boolean) { + return factory.createFalse(); + } + if (type.flags & TypeFlags.EnumLike) { + const enumMember = type.symbol.exports ? firstOrUndefined(arrayFrom(type.symbol.exports.values())) : type.symbol; + const name = checker.symbolToExpression(type.symbol.parent ? type.symbol.parent : type.symbol, SymbolFlags.Value, /*enclosingDeclaration*/ undefined, /*flags*/ undefined); + return enumMember === undefined || name === undefined ? factory.createNumericLiteral(0) : factory.createPropertyAccessExpression(name, checker.symbolToString(enumMember)); + } + if (type.flags & TypeFlags.NumberLiteral) { + return factory.createNumericLiteral((type as NumberLiteralType).value); + } + if (type.flags & TypeFlags.BigIntLiteral) { + return factory.createBigIntLiteral((type as BigIntLiteralType).value); + } + if (type.flags & TypeFlags.StringLiteral) { + return factory.createStringLiteral((type as StringLiteralType).value, /* isSingleQuote */ quotePreference === QuotePreference.Single); + } + if (type.flags & TypeFlags.BooleanLiteral) { + return (type === checker.getFalseType() || type === checker.getFalseType(/*fresh*/ true)) ? factory.createFalse() : factory.createTrue(); + } + if (type.flags & TypeFlags.Null) { + return factory.createNull(); + } + if (type.flags & TypeFlags.Union) { + const expression = firstDefined((type as UnionType).types, t => tryGetInitializerValueFromType(context, checker, importAdder, quotePreference, t)); + return expression ?? createUndefined(); + } + if (checker.isArrayLikeType(type)) { + return factory.createArrayLiteralExpression(); + } + if (isObjectLiteralType(type)) { + const props = map(checker.getPropertiesOfType(type), prop => { + const initializer = prop.valueDeclaration ? tryGetInitializerValueFromType(context, checker, importAdder, quotePreference, checker.getTypeAtLocation(prop.valueDeclaration)) : createUndefined(); + return factory.createPropertyAssignment(prop.name, initializer); + }); + return factory.createObjectLiteralExpression(props, /*multiLine*/ true); + } + if (getObjectFlags(type) & ObjectFlags.Anonymous) { + const decl = find(type.symbol.declarations || emptyArray, or(isFunctionTypeNode, isMethodSignature, isMethodDeclaration)); + if (decl === undefined) return createUndefined(); + + const signature = checker.getSignaturesOfType(type, SignatureKind.Call); + if (signature === undefined) return createUndefined(); + + const func = createSignatureDeclarationFromSignature(SyntaxKind.FunctionExpression, context, quotePreference, signature[0], + createStubbedBody(Diagnostics.Function_not_implemented.message, quotePreference), /*name*/ undefined, /*modifiers*/ undefined, /*optional*/ undefined, /*enclosingDeclaration*/ undefined, importAdder) as FunctionExpression | undefined; + return func ?? createUndefined(); + } + if (getObjectFlags(type) & ObjectFlags.Class) { + const classDeclaration = getClassLikeDeclarationOfSymbol(type.symbol); + if (classDeclaration === undefined || hasAbstractModifier(classDeclaration)) return createUndefined(); + + const constructorDeclaration = getFirstConstructorWithBody(classDeclaration); + if (constructorDeclaration && length(constructorDeclaration.parameters)) return createUndefined(); + + return factory.createNewExpression(factory.createIdentifier(type.symbol.name), /*typeArguments*/ undefined, /*argumentsArray*/ undefined); + } + return createUndefined(); + } + + function createUndefined() { + return factory.createIdentifier("undefined"); + } + + function isObjectLiteralType(type: Type) { + return (type.flags & TypeFlags.Object) && + ((getObjectFlags(type) & ObjectFlags.ObjectLiteral) || (type.symbol && tryCast(singleOrUndefined(type.symbol.declarations), isTypeLiteralNode))); + } } diff --git a/src/services/codefixes/helpers.ts b/src/services/codefixes/helpers.ts index 229546f1018d9..4716b03e13b8a 100644 --- a/src/services/codefixes/helpers.ts +++ b/src/services/codefixes/helpers.ts @@ -145,27 +145,28 @@ namespace ts.codefix { } function outputMethod(quotePreference: QuotePreference, signature: Signature, modifiers: NodeArray | undefined, name: PropertyName, body?: Block): void { - const method = signatureToMethodDeclaration(context, quotePreference, signature, enclosingDeclaration, modifiers, name, optional, body, importAdder); + const method = createSignatureDeclarationFromSignature(SyntaxKind.MethodDeclaration, context, quotePreference, signature, body, name, modifiers, optional, enclosingDeclaration, importAdder); if (method) addClassElement(method); } } - function signatureToMethodDeclaration( + export function createSignatureDeclarationFromSignature( + kind: SyntaxKind.MethodDeclaration | SyntaxKind.FunctionExpression | SyntaxKind.ArrowFunction, context: TypeConstructionContext, quotePreference: QuotePreference, signature: Signature, - enclosingDeclaration: ClassLikeDeclaration, - modifiers: NodeArray | undefined, - name: PropertyName, - optional: boolean, body: Block | undefined, - importAdder: ImportAdder | undefined, - ): MethodDeclaration | undefined { + name: PropertyName | undefined, + modifiers: NodeArray | undefined, + optional: boolean | undefined, + enclosingDeclaration: Node | undefined, + importAdder: ImportAdder | undefined + ) { const program = context.program; const checker = program.getTypeChecker(); const scriptTarget = getEmitScriptTarget(program.getCompilerOptions()); const flags = NodeBuilderFlags.NoTruncation | NodeBuilderFlags.NoUndefinedOptionalParameterType | NodeBuilderFlags.SuppressAnyReturnType | (quotePreference === QuotePreference.Single ? NodeBuilderFlags.UseSingleQuotesForStringLiteralType : 0); - const signatureDeclaration = checker.signatureToSignatureDeclaration(signature, SyntaxKind.MethodDeclaration, enclosingDeclaration, flags, getNoopSymbolTrackerWithResolver(context)) as MethodDeclaration; + const signatureDeclaration = checker.signatureToSignatureDeclaration(signature, kind, enclosingDeclaration, flags, getNoopSymbolTrackerWithResolver(context)) as ArrowFunction | FunctionExpression | MethodDeclaration; if (!signatureDeclaration) { return undefined; } @@ -233,18 +234,18 @@ namespace ts.codefix { } } - return factory.updateMethodDeclaration( - signatureDeclaration, - /*decorators*/ undefined, - modifiers, - signatureDeclaration.asteriskToken, - name, - optional ? factory.createToken(SyntaxKind.QuestionToken) : undefined, - typeParameters, - parameters, - type, - body - ); + const questionToken = optional ? factory.createToken(SyntaxKind.QuestionToken) : undefined; + const asteriskToken = signatureDeclaration.asteriskToken; + if (isFunctionExpression(signatureDeclaration)) { + return factory.updateFunctionExpression(signatureDeclaration, modifiers, signatureDeclaration.asteriskToken, tryCast(name, isIdentifier), typeParameters, parameters, type, body ?? signatureDeclaration.body); + } + if (isArrowFunction(signatureDeclaration)) { + return factory.updateArrowFunction(signatureDeclaration, modifiers, typeParameters, parameters, type, signatureDeclaration.equalsGreaterThanToken, body ?? signatureDeclaration.body); + } + if (isMethodDeclaration(signatureDeclaration)) { + return factory.updateMethodDeclaration(signatureDeclaration, /* decorators */ undefined, modifiers, asteriskToken, name ?? factory.createIdentifier(""), questionToken, typeParameters, parameters, type, body); + } + return undefined; } export function createSignatureDeclarationFromCallExpression( diff --git a/tests/cases/fourslash/codeFixAddMissingProperties1.ts b/tests/cases/fourslash/codeFixAddMissingProperties1.ts new file mode 100644 index 0000000000000..0d82892c02ca1 --- /dev/null +++ b/tests/cases/fourslash/codeFixAddMissingProperties1.ts @@ -0,0 +1,39 @@ +/// + +////interface Foo { +//// a: number; +//// b: string; +//// c: 1; +//// d: "d"; +//// e: "e1" | "e2"; +//// f(x: number, y: number): void; +//// g: (x: number, y: number) => void; +//// h: number[]; +//// i: bigint; +//// j: undefined | "special-string"; +//// k: `--${string}`; +////} +////[|const foo: Foo = {}|]; + +verify.codeFix({ + index: 0, + description: ts.Diagnostics.Add_missing_properties.message, + newRangeContent: +`const foo: Foo = { + a: 0, + b: "", + c: 1, + d: "d", + e: "e1", + f: function(x: number, y: number): void { + throw new Error("Function not implemented."); + }, + g: function(x: number, y: number): void { + throw new Error("Function not implemented."); + }, + h: [], + i: 0n, + j: "special-string", + k: "" +}` +}); diff --git a/tests/cases/fourslash/codeFixAddMissingProperties10.ts b/tests/cases/fourslash/codeFixAddMissingProperties10.ts new file mode 100644 index 0000000000000..6bffc3c64a53f --- /dev/null +++ b/tests/cases/fourslash/codeFixAddMissingProperties10.ts @@ -0,0 +1,18 @@ +/// + +////type T = { x: number; }; +////interface I { +//// a: T +////} +////[|const foo: I = {};|] + +verify.codeFix({ + index: 0, + description: ts.Diagnostics.Add_missing_properties.message, + newRangeContent: +`const foo: I = { + a: { + x: 0 + } +};` +}); diff --git a/tests/cases/fourslash/codeFixAddMissingProperties11.ts b/tests/cases/fourslash/codeFixAddMissingProperties11.ts new file mode 100644 index 0000000000000..a64c208cc934f --- /dev/null +++ b/tests/cases/fourslash/codeFixAddMissingProperties11.ts @@ -0,0 +1,22 @@ +/// + +////interface Foo { +//// a: `--${string}`; +//// b: string; +//// c: "a" | "b" +////} +////[|const foo: Foo = {}|]; + +verify.codeFix({ + index: 0, + description: ts.Diagnostics.Add_missing_properties.message, + preferences: { + quotePreference: "single" + }, + newRangeContent: +`const foo: Foo = { + a: '', + b: '', + c: 'a' +}` +}); diff --git a/tests/cases/fourslash/codeFixAddMissingProperties2.ts b/tests/cases/fourslash/codeFixAddMissingProperties2.ts new file mode 100644 index 0000000000000..d1ceabf148d9d --- /dev/null +++ b/tests/cases/fourslash/codeFixAddMissingProperties2.ts @@ -0,0 +1,23 @@ +/// + +////interface Foo { +//// a: number; +//// b: string; +//// c: any; +////} +////[|class C { +//// public c: Foo = {}; +////}|] + +verify.codeFix({ + index: 0, + description: ts.Diagnostics.Add_missing_properties.message, + newRangeContent: +`class C { + public c: Foo = { + a: 0, + b: "", + c: undefined + }; +}` +}); diff --git a/tests/cases/fourslash/codeFixAddMissingProperties3.ts b/tests/cases/fourslash/codeFixAddMissingProperties3.ts new file mode 100644 index 0000000000000..58481e5485986 --- /dev/null +++ b/tests/cases/fourslash/codeFixAddMissingProperties3.ts @@ -0,0 +1,19 @@ +/// + +////interface Foo { +//// a: number; +//// b: string; +////} +////[|function fn(foo: Foo = {}) { +////}|] + +verify.codeFix({ + index: 0, + description: ts.Diagnostics.Add_missing_properties.message, + newRangeContent: +`function fn(foo: Foo = { + a: 0, + b: "" +}) { +}` +}); diff --git a/tests/cases/fourslash/codeFixAddMissingProperties4.ts b/tests/cases/fourslash/codeFixAddMissingProperties4.ts new file mode 100644 index 0000000000000..10acdf0a3243c --- /dev/null +++ b/tests/cases/fourslash/codeFixAddMissingProperties4.ts @@ -0,0 +1,17 @@ +/// + +////interface Foo { +//// a: number; +//// b: string; +////} +////[|const foo: Foo = { a: 10 }|]; + +verify.codeFix({ + index: 0, + description: ts.Diagnostics.Add_missing_properties.message, + newRangeContent: +`const foo: Foo = { + a: 10, + b: "" +}` +}); diff --git a/tests/cases/fourslash/codeFixAddMissingProperties5.ts b/tests/cases/fourslash/codeFixAddMissingProperties5.ts new file mode 100644 index 0000000000000..7294f4c0d90fb --- /dev/null +++ b/tests/cases/fourslash/codeFixAddMissingProperties5.ts @@ -0,0 +1,16 @@ +/// + +////type T = { +//// a: null; +////} +//// +////[|const foo: T = {}|]; + +verify.codeFix({ + index: 0, + description: ts.Diagnostics.Add_missing_properties.message, + newRangeContent: +`const foo: T = { + a: null +}` +}); diff --git a/tests/cases/fourslash/codeFixAddMissingProperties6.ts b/tests/cases/fourslash/codeFixAddMissingProperties6.ts new file mode 100644 index 0000000000000..d4d4e2eb8dd98 --- /dev/null +++ b/tests/cases/fourslash/codeFixAddMissingProperties6.ts @@ -0,0 +1,23 @@ +/// + +////interface I { +//// x: number; +//// y: number; +////} +////class C { +//// public p: number; +//// m(x: number, y: I) {} +////} +////[|const foo: C = {}|]; + +verify.codeFix({ + index: 0, + description: ts.Diagnostics.Add_missing_properties.message, + newRangeContent: +`const foo: C = { + p: 0, + m: function(x: number, y: I): void { + throw new Error("Function not implemented."); + } +}` +}); diff --git a/tests/cases/fourslash/codeFixAddMissingProperties7.ts b/tests/cases/fourslash/codeFixAddMissingProperties7.ts new file mode 100644 index 0000000000000..9ccfdaf6f8a70 --- /dev/null +++ b/tests/cases/fourslash/codeFixAddMissingProperties7.ts @@ -0,0 +1,27 @@ +/// + +////enum E1 { +//// A, B +////} +////enum E2 { +//// A +////} +////enum E3 { +////} +////interface I { +//// x: E1; +//// y: E2; +//// z: E3; +////} +////[|const foo: I = {};|] + +verify.codeFix({ + index: 0, + description: ts.Diagnostics.Add_missing_properties.message, + newRangeContent: +`const foo: I = { + x: E1.A, + y: E2.A, + z: 0 +};` +}); diff --git a/tests/cases/fourslash/codeFixAddMissingProperties8.ts b/tests/cases/fourslash/codeFixAddMissingProperties8.ts new file mode 100644 index 0000000000000..07a144cfc8e30 --- /dev/null +++ b/tests/cases/fourslash/codeFixAddMissingProperties8.ts @@ -0,0 +1,29 @@ +/// + +////class A { +//// constructor() {} +////} +//// +////abstract class B {} +//// +////class C { +//// constructor(a: string, b: number, c: A) {} +////} +//// +////interface I { +//// a: A; +//// b: B; +//// c: C; +////} +////[|const foo: I = {};|] + +verify.codeFix({ + index: 0, + description: ts.Diagnostics.Add_missing_properties.message, + newRangeContent: +`const foo: I = { + a: new A, + b: undefined, + c: undefined +};` +}); diff --git a/tests/cases/fourslash/codeFixAddMissingProperties9.ts b/tests/cases/fourslash/codeFixAddMissingProperties9.ts new file mode 100644 index 0000000000000..b6291141c2503 --- /dev/null +++ b/tests/cases/fourslash/codeFixAddMissingProperties9.ts @@ -0,0 +1,23 @@ +/// + +////interface I { +//// a: { +//// x: number; +//// y: { z: string; }; +//// } +////} +////[|const foo: I = {};|] + +verify.codeFix({ + index: 0, + description: ts.Diagnostics.Add_missing_properties.message, + newRangeContent: +`const foo: I = { + a: { + x: 0, + y: { + z: "" + } + } +};` +}); diff --git a/tests/cases/fourslash/codeFixAddMissingProperties_all.ts b/tests/cases/fourslash/codeFixAddMissingProperties_all.ts new file mode 100644 index 0000000000000..f0e842e005530 --- /dev/null +++ b/tests/cases/fourslash/codeFixAddMissingProperties_all.ts @@ -0,0 +1,78 @@ +/// + +////interface I1 { +//// a: number; +//// b: string; +//// c: 1; +//// d: "d"; +//// e: "e1" | "e2"; +//// f(x: number, y: number): void; +//// g: (x: number, y: number) => void; +////} +////interface I2 { +//// a: unknown; +//// b: any; +////} +////const a: I1 = {}; +////const b: I2 = {}; +////class C { +//// public c: I1 = {}; +////} +////function fn(foo: I2 = {}) { +////} + +verify.codeFixAll({ + fixId: "fixMissingProperties", + fixAllDescription: ts.Diagnostics.Add_all_missing_properties.message, + newFileContent: +`interface I1 { + a: number; + b: string; + c: 1; + d: "d"; + e: "e1" | "e2"; + f(x: number, y: number): void; + g: (x: number, y: number) => void; +} +interface I2 { + a: unknown; + b: any; +} +const a: I1 = { + a: 0, + b: "", + c: 1, + d: "d", + e: "e1", + f: function(x: number, y: number): void { + throw new Error("Function not implemented."); + }, + g: function(x: number, y: number): void { + throw new Error("Function not implemented."); + } +}; +const b: I2 = { + a: undefined, + b: undefined +}; +class C { + public c: I1 = { + a: 0, + b: "", + c: 1, + d: "d", + e: "e1", + f: function(x: number, y: number): void { + throw new Error("Function not implemented."); + }, + g: function(x: number, y: number): void { + throw new Error("Function not implemented."); + } + }; +} +function fn(foo: I2 = { + a: undefined, + b: undefined +}) { +}` +});