diff --git a/src/services/codefixes/fixAddMissingMember.ts b/src/services/codefixes/fixAddMissingMember.ts
index c57d2a37fd355..471cbab82c6c9 100644
--- a/src/services/codefixes/fixAddMissingMember.ts
+++ b/src/services/codefixes/fixAddMissingMember.ts
@@ -79,11 +79,13 @@ import {
isPropertyAccessExpression,
isPropertyDeclaration,
isReturnStatement,
+ isSatisfiesExpression,
isSourceFile,
isSourceFileFromLibrary,
isSourceFileJS,
isTransientSymbol,
isTypeLiteralNode,
+ isYieldExpression,
JsxOpeningLikeElement,
LanguageVariant,
lastOrUndefined,
@@ -141,6 +143,7 @@ const errorCodes = [
Diagnostics.Type_0_is_missing_the_following_properties_from_type_1_Colon_2_and_3_more.code,
Diagnostics.Argument_of_type_0_is_not_assignable_to_parameter_of_type_1.code,
Diagnostics.Cannot_find_name_0.code,
+ Diagnostics.Type_0_does_not_satisfy_the_expected_type_1.code,
];
enum InfoKind {
@@ -188,9 +191,11 @@ registerCodeFix({
return createCombinedCodeActions(textChanges.ChangeTracker.with(context, changes => {
eachDiagnostic(context, errorCodes, diag => {
const info = getInfo(diag.file, diag.start, diag.code, checker, context.program);
- if (!info || !addToSeen(seen, getNodeId(info.parentDeclaration) + "#" + (info.kind === InfoKind.ObjectLiteral ? info.identifier : info.token.text))) {
- return;
- }
+ if (info === undefined) return;
+
+ const nodeId = getNodeId(info.parentDeclaration) + "#" + (info.kind === InfoKind.ObjectLiteral ? info.identifier || getNodeId(info.token) : info.token.text);
+ if (!addToSeen(seen, nodeId)) return;
+
if (fixId === fixMissingFunctionDeclaration && (info.kind === InfoKind.Function || info.kind === InfoKind.Signature)) {
addFunctionDeclaration(changes, context, info);
}
@@ -275,7 +280,7 @@ interface FunctionInfo {
interface ObjectLiteralInfo {
readonly kind: InfoKind.ObjectLiteral;
readonly token: Node;
- readonly identifier: string;
+ readonly identifier: string | undefined;
readonly properties: Symbol[];
readonly parentDeclaration: ObjectLiteralExpression;
readonly indentation?: number;
@@ -320,15 +325,16 @@ function getInfo(sourceFile: SourceFile, tokenPos: number, errorCode: number, ch
return { kind: InfoKind.ObjectLiteral, token: param.name, identifier: param.name.text, properties, parentDeclaration: parent };
}
- if (token.kind === SyntaxKind.OpenBraceToken && isObjectLiteralExpression(parent)) {
- const targetType = (checker.getContextualType(parent) || checker.getTypeAtLocation(parent))?.getNonNullableType();
- const properties = arrayFrom(checker.getUnmatchedProperties(checker.getTypeAtLocation(parent), targetType, /*requireOptionalProperties*/ false, /*matchDiscriminantProperties*/ false));
- if (!length(properties)) return undefined;
-
- // no identifier needed because the whole parentDeclaration has the error
- const identifier = "";
+ if (token.kind === SyntaxKind.OpenBraceToken || isSatisfiesExpression(parent) || isReturnStatement(parent)) {
+ const expression = (isSatisfiesExpression(parent) || isReturnStatement(parent)) && parent.expression ? parent.expression : parent;
+ if (isObjectLiteralExpression(expression)) {
+ const targetType = isSatisfiesExpression(parent) ? checker.getTypeFromTypeNode(parent.type) :
+ checker.getContextualType(expression) || checker.getTypeAtLocation(expression);
+ const properties = arrayFrom(checker.getUnmatchedProperties(checker.getTypeAtLocation(parent), targetType.getNonNullableType(), /*requireOptionalProperties*/ false, /*matchDiscriminantProperties*/ false));
+ if (!length(properties)) return undefined;
- return { kind: InfoKind.ObjectLiteral, token: parent, identifier, properties, parentDeclaration: parent };
+ return { kind: InfoKind.ObjectLiteral, token: parent, identifier: undefined, properties, parentDeclaration: expression, indentation: isReturnStatement(expression.parent) || isYieldExpression(expression.parent) ? 0 : undefined };
+ }
}
if (!isMemberName(token)) return undefined;
diff --git a/tests/cases/fourslash/codeFixAddMissingProperties33.ts b/tests/cases/fourslash/codeFixAddMissingProperties33.ts
new file mode 100644
index 0000000000000..c034471a4205a
--- /dev/null
+++ b/tests/cases/fourslash/codeFixAddMissingProperties33.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 b = {} satisfies Foo;|]
+
+verify.codeFix({
+ index: 0,
+ description: ts.Diagnostics.Add_missing_properties.message,
+ newRangeContent:
+`const b = {
+ 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: ""
+} satisfies Foo;`,
+});
diff --git a/tests/cases/fourslash/codeFixAddMissingProperties34.ts b/tests/cases/fourslash/codeFixAddMissingProperties34.ts
new file mode 100644
index 0000000000000..a74658c2ed5c1
--- /dev/null
+++ b/tests/cases/fourslash/codeFixAddMissingProperties34.ts
@@ -0,0 +1,23 @@
+///
+
+////interface Foo {
+//// a: number;
+//// b: string;
+//// c: any;
+////}
+////[|class C {
+//// public c = {} satisfies Foo;
+////}|]
+
+verify.codeFix({
+ index: 0,
+ description: ts.Diagnostics.Add_missing_properties.message,
+ newRangeContent:
+`class C {
+ public c = {
+ a: 0,
+ b: "",
+ c: undefined
+ } satisfies Foo;
+}`
+});
diff --git a/tests/cases/fourslash/codeFixAddMissingProperties35.ts b/tests/cases/fourslash/codeFixAddMissingProperties35.ts
new file mode 100644
index 0000000000000..f9b120cba71c2
--- /dev/null
+++ b/tests/cases/fourslash/codeFixAddMissingProperties35.ts
@@ -0,0 +1,17 @@
+///
+
+////interface Foo {
+//// a: number;
+//// b: string;
+////}
+////[|const foo = { a: 10 } satisfies Foo;|]
+
+verify.codeFix({
+ index: 0,
+ description: ts.Diagnostics.Add_missing_properties.message,
+ newRangeContent:
+`const foo = {
+ a: 10,
+ b: ""
+} satisfies Foo;`
+});
diff --git a/tests/cases/fourslash/codeFixAddMissingProperties36.ts b/tests/cases/fourslash/codeFixAddMissingProperties36.ts
new file mode 100644
index 0000000000000..629943c1cf3a8
--- /dev/null
+++ b/tests/cases/fourslash/codeFixAddMissingProperties36.ts
@@ -0,0 +1,16 @@
+///
+
+////type T = {
+//// a: null;
+////}
+////
+////[|const foo = {} satisfies T;|]
+
+verify.codeFix({
+ index: 0,
+ description: ts.Diagnostics.Add_missing_properties.message,
+ newRangeContent:
+`const foo = {
+ a: null
+} satisfies T;`
+});
diff --git a/tests/cases/fourslash/codeFixAddMissingProperties37.ts b/tests/cases/fourslash/codeFixAddMissingProperties37.ts
new file mode 100644
index 0000000000000..cdc0b7e82f775
--- /dev/null
+++ b/tests/cases/fourslash/codeFixAddMissingProperties37.ts
@@ -0,0 +1,23 @@
+///
+
+////interface I {
+//// x: number;
+//// y: number;
+////}
+////class C {
+//// public p: number;
+//// m(x: number, y: I) {}
+////}
+////[|const foo = {} satisfies C;|]
+
+verify.codeFix({
+ index: 0,
+ description: ts.Diagnostics.Add_missing_properties.message,
+ newRangeContent:
+`const foo = {
+ p: 0,
+ m: function(x: number, y: I): void {
+ throw new Error("Function not implemented.");
+ }
+} satisfies C;`
+});
diff --git a/tests/cases/fourslash/codeFixAddMissingProperties38.ts b/tests/cases/fourslash/codeFixAddMissingProperties38.ts
new file mode 100644
index 0000000000000..0932e6581f0c9
--- /dev/null
+++ b/tests/cases/fourslash/codeFixAddMissingProperties38.ts
@@ -0,0 +1,27 @@
+///
+
+////enum E1 {
+//// A, B
+////}
+////enum E2 {
+//// A
+////}
+////enum E3 {
+////}
+////interface I {
+//// x: E1;
+//// y: E2;
+//// z: E3;
+////}
+////[|const foo = {} satisfies I;|]
+
+verify.codeFix({
+ index: 0,
+ description: ts.Diagnostics.Add_missing_properties.message,
+ newRangeContent:
+`const foo = {
+ x: E1.A,
+ y: E2.A,
+ z: 0
+} satisfies I;`
+});
diff --git a/tests/cases/fourslash/codeFixAddMissingProperties39.ts b/tests/cases/fourslash/codeFixAddMissingProperties39.ts
new file mode 100644
index 0000000000000..d7f99cbfcb8c7
--- /dev/null
+++ b/tests/cases/fourslash/codeFixAddMissingProperties39.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 = {} satisfies I;|]
+
+verify.codeFix({
+ index: 0,
+ description: ts.Diagnostics.Add_missing_properties.message,
+ newRangeContent:
+`const foo = {
+ a: new A,
+ b: undefined,
+ c: undefined
+} satisfies I;`
+});
diff --git a/tests/cases/fourslash/codeFixAddMissingProperties40.ts b/tests/cases/fourslash/codeFixAddMissingProperties40.ts
new file mode 100644
index 0000000000000..ca640fe0ec8f5
--- /dev/null
+++ b/tests/cases/fourslash/codeFixAddMissingProperties40.ts
@@ -0,0 +1,23 @@
+///
+
+////interface I {
+//// a: {
+//// x: number;
+//// y: { z: string; };
+//// }
+////}
+////[|const foo = {} satisfies I;|]
+
+verify.codeFix({
+ index: 0,
+ description: ts.Diagnostics.Add_missing_properties.message,
+ newRangeContent:
+`const foo = {
+ a: {
+ x: 0,
+ y: {
+ z: ""
+ }
+ }
+} satisfies I;`
+});
diff --git a/tests/cases/fourslash/codeFixAddMissingProperties41.ts b/tests/cases/fourslash/codeFixAddMissingProperties41.ts
new file mode 100644
index 0000000000000..42330c13daa7d
--- /dev/null
+++ b/tests/cases/fourslash/codeFixAddMissingProperties41.ts
@@ -0,0 +1,21 @@
+///
+
+////interface Bar {
+//// a: number;
+////}
+////
+////interface Foo {
+//// foo(a: T): U;
+////}
+////[|const x = {} satisfies Foo;|]
+
+verify.codeFix({
+ index: 0,
+ description: ts.Diagnostics.Add_missing_properties.message,
+ newRangeContent:
+`const x = {
+ foo: function(a: string): Bar {
+ throw new Error("Function not implemented.");
+ }
+} satisfies Foo;`,
+});
diff --git a/tests/cases/fourslash/codeFixAddMissingProperties42.ts b/tests/cases/fourslash/codeFixAddMissingProperties42.ts
new file mode 100644
index 0000000000000..184a36914c041
--- /dev/null
+++ b/tests/cases/fourslash/codeFixAddMissingProperties42.ts
@@ -0,0 +1,15 @@
+///
+
+////type A = { a: string };
+////type B = { b: string };
+////
+////[|const c = { } satisfies A satisfies B;|]
+
+verify.codeFix({
+ index: 0,
+ description: ts.Diagnostics.Add_missing_properties.message,
+ newRangeContent:
+`const c = {
+ a: ""
+} satisfies A satisfies B;`,
+});
diff --git a/tests/cases/fourslash/codeFixAddMissingProperties43.ts b/tests/cases/fourslash/codeFixAddMissingProperties43.ts
new file mode 100644
index 0000000000000..bf50ec73974c9
--- /dev/null
+++ b/tests/cases/fourslash/codeFixAddMissingProperties43.ts
@@ -0,0 +1,41 @@
+///
+
+////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 f = (): Foo => {
+//// [|return { };|]
+////};
+
+verify.codeFix({
+ index: 0,
+ description: ts.Diagnostics.Add_missing_properties.message,
+ newRangeContent:
+`return {
+ 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/codeFixAddMissingProperties44.ts b/tests/cases/fourslash/codeFixAddMissingProperties44.ts
new file mode 100644
index 0000000000000..5cb87bd2a53d9
--- /dev/null
+++ b/tests/cases/fourslash/codeFixAddMissingProperties44.ts
@@ -0,0 +1,43 @@
+///
+// @lib: es2020
+// @target: es2020
+
+////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 f = function* (): Generator {
+//// [|yield {};|]
+////};
+
+verify.codeFix({
+ index: 0,
+ description: ts.Diagnostics.Add_missing_properties.message,
+ newRangeContent:
+`yield {
+ 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/codeFixAddMissingProperties_all.ts b/tests/cases/fourslash/codeFixAddMissingProperties_all.ts
index 78c7e6d165674..aacfe48ddd450 100644
--- a/tests/cases/fourslash/codeFixAddMissingProperties_all.ts
+++ b/tests/cases/fourslash/codeFixAddMissingProperties_all.ts
@@ -15,12 +15,17 @@
////}
////const a: I1 = {};
////const b: I2 = {};
-////class C {
+////class C1 {
//// public c: I1 = {};
////}
////function fn1(foo: I2 = {}) {}
////function fn2(a: I1) {}
////fn2({});
+////const d = {} satisfies I1;
+////const e = {} satisfies I2;
+////class C2 {
+//// public f = {} satisfies I1;
+////}
verify.codeFixAll({
fixId: "fixMissingProperties",
@@ -56,7 +61,7 @@ const b: I2 = {
a: undefined,
b: undefined
};
-class C {
+class C1 {
public c: I1 = {
a: 0,
b: "",
@@ -88,5 +93,37 @@ fn2({
g: function(x: number, y: number): void {
throw new Error("Function not implemented.");
}
-});`
+});
+const d = {
+ 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.");
+ }
+} satisfies I1;
+const e = {
+ a: undefined,
+ b: undefined
+} satisfies I2;
+class C2 {
+ public f = {
+ 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.");
+ }
+ } satisfies I1;
+}`
});