feat(60312): Add missing properties for satisfies (#60314)

This commit is contained in:
Oleksandr T. 2024-11-08 19:48:13 +02:00 коммит произвёл GitHub
Родитель 55f1248a20
Коммит ef802b1e4d
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: B5690EEEBB952194
14 изменённых файлов: 375 добавлений и 15 удалений

Просмотреть файл

@ -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;
}`
});