feat(60312): Add missing properties for satisfies (#60314)
This commit is contained in:
Родитель
55f1248a20
Коммит
ef802b1e4d
|
@ -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;
|
||||
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;
|
||||
|
||||
// no identifier needed because the whole parentDeclaration has the error
|
||||
const identifier = "";
|
||||
|
||||
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;
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
/// <reference path="fourslash.ts" />
|
||||
|
||||
////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;`,
|
||||
});
|
|
@ -0,0 +1,23 @@
|
|||
/// <reference path='fourslash.ts' />
|
||||
|
||||
////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;
|
||||
}`
|
||||
});
|
|
@ -0,0 +1,17 @@
|
|||
/// <reference path='fourslash.ts' />
|
||||
|
||||
////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;`
|
||||
});
|
|
@ -0,0 +1,16 @@
|
|||
/// <reference path='fourslash.ts' />
|
||||
|
||||
////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;`
|
||||
});
|
|
@ -0,0 +1,23 @@
|
|||
/// <reference path='fourslash.ts' />
|
||||
|
||||
////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;`
|
||||
});
|
|
@ -0,0 +1,27 @@
|
|||
/// <reference path='fourslash.ts' />
|
||||
|
||||
////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;`
|
||||
});
|
|
@ -0,0 +1,29 @@
|
|||
/// <reference path='fourslash.ts' />
|
||||
|
||||
////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;`
|
||||
});
|
|
@ -0,0 +1,23 @@
|
|||
/// <reference path='fourslash.ts' />
|
||||
|
||||
////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;`
|
||||
});
|
|
@ -0,0 +1,21 @@
|
|||
/// <reference path='fourslash.ts' />
|
||||
|
||||
////interface Bar {
|
||||
//// a: number;
|
||||
////}
|
||||
////
|
||||
////interface Foo<T, U> {
|
||||
//// foo(a: T): U;
|
||||
////}
|
||||
////[|const x = {} satisfies Foo<string, Bar>;|]
|
||||
|
||||
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<string, Bar>;`,
|
||||
});
|
|
@ -0,0 +1,15 @@
|
|||
/// <reference path='fourslash.ts' />
|
||||
|
||||
////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;`,
|
||||
});
|
|
@ -0,0 +1,41 @@
|
|||
/// <reference path='fourslash.ts' />
|
||||
|
||||
////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: ""
|
||||
};`,
|
||||
});
|
|
@ -0,0 +1,43 @@
|
|||
/// <reference path='fourslash.ts' />
|
||||
// @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<Foo, void, any> {
|
||||
//// [|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: ""
|
||||
};`,
|
||||
});
|
|
@ -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;
|
||||
}`
|
||||
});
|
||||
|
|
Загрузка…
Ссылка в новой задаче