Skip to content

Commit 4b7b3fe

Browse files
committed
Simplify: add undefined to all optional propertie
whether or not somebody tried to assign undefined to them in the erroneous assignment
1 parent 3e70798 commit 4b7b3fe

File tree

6 files changed

+52
-25
lines changed

6 files changed

+52
-25
lines changed

src/compiler/checker.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -634,8 +634,7 @@ namespace ts {
634634
isTupleType,
635635
isArrayLikeType,
636636
isTypeInvalidDueToUnionDiscriminant,
637-
getExactOptionalUnassignableProperties,
638-
isExactOptionalPropertyMismatch,
637+
getExactOptionalProperties,
639638
getAllPossiblePropertiesOfTypes,
640639
getSuggestedSymbolForNonexistentProperty,
641640
getSuggestionForNonexistentProperty,
@@ -19589,6 +19588,10 @@ namespace ts {
1958919588
return !!source && !!target && maybeTypeOfKind(source, TypeFlags.Undefined) && !!containsMissingType(target);
1959019589
}
1959119590

19591+
function getExactOptionalProperties(type: Type) {
19592+
return getPropertiesOfType(type).filter(targetProp => containsMissingType(getTypeOfSymbol(targetProp)));
19593+
}
19594+
1959219595
function getBestMatchingType(source: Type, target: UnionOrIntersectionType, isRelatedTo = compareTypesAssignable) {
1959319596
return findMatchingDiscriminantType(source, target, isRelatedTo, /*skipPartial*/ true) ||
1959419597
findMatchingTypeReferenceOrTypeAliasReference(source, target) ||

src/compiler/types.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4307,8 +4307,7 @@ namespace ts {
43074307
* e.g. it specifies `kind: "a"` and obj has `kind: "b"`.
43084308
*/
43094309
/* @internal */ isTypeInvalidDueToUnionDiscriminant(contextualType: Type, obj: ObjectLiteralExpression | JsxAttributes): boolean;
4310-
/* @internal */ getExactOptionalUnassignableProperties(source: Type, target: Type): Symbol[];
4311-
/* @internal */ isExactOptionalPropertyMismatch(source: Type | undefined, target: Type | undefined): boolean;
4310+
/* @internal */ getExactOptionalProperties(type: Type): Symbol[];
43124311
/**
43134312
* For a union, will include a property if it's defined in *any* of the member types.
43144313
* So for `{ a } | { b }`, this will include both `a` and `b`.

src/services/codefixes/addOptionalPropertyUndefined.ts

Lines changed: 8 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -50,27 +50,19 @@ namespace ts.codefix {
5050
return emptyArray;
5151
}
5252
const { source: sourceNode, target: targetNode } = sourceTarget;
53-
const target = checker.getTypeAtLocation(targetNode);
54-
const source = checker.getTypeAtLocation(sourceNode);
53+
const target = shouldUseParentTypeOfProperty(sourceNode, targetNode, checker)
54+
? checker.getTypeAtLocation(targetNode.expression)
55+
: checker.getTypeAtLocation(targetNode);
5556
if (target.symbol?.declarations?.some(d => getSourceFileOfNode(d).fileName.match(/\.d\.ts$/))) {
5657
return emptyArray;
5758
}
58-
const targetPropertyType = getTargetPropertyType(checker, targetNode);
59-
if (targetPropertyType && checker.isExactOptionalPropertyMismatch(source, targetPropertyType)) {
60-
const s = checker.getSymbolAtLocation((targetNode as PropertyAccessExpression).name);
61-
return s ? [s] : emptyArray;
62-
}
63-
return checker.getExactOptionalUnassignableProperties(source, target);
59+
return checker.getExactOptionalProperties(target);
6460
}
6561

66-
function getTargetPropertyType(checker: TypeChecker, targetNode: Node) {
67-
if (isPropertySignature(targetNode)) {
68-
return checker.getTypeAtLocation(targetNode.name);
69-
}
70-
else if (isPropertyAccessExpression(targetNode)) {
71-
return checker.getTypeOfPropertyOfType(checker.getTypeAtLocation(targetNode.expression), targetNode.name.text);
72-
}
73-
return undefined;
62+
function shouldUseParentTypeOfProperty(sourceNode: Node, targetNode: Node, checker: TypeChecker): targetNode is PropertyAccessExpression {
63+
return isPropertyAccessExpression(targetNode)
64+
&& !!checker.getExactOptionalProperties(checker.getTypeAtLocation(targetNode.expression)).length
65+
&& checker.getTypeAtLocation(sourceNode) === checker.getUndefinedType();
7466
}
7567

7668
/**

tests/cases/fourslash/fixExactOptionalUnassignableProperties2.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ interface ID {
105105
}
106106
interface More {
107107
a?: number | undefined
108-
b?: number
108+
b?: number | undefined
109109
}
110110
interface Assignment {
111111
a?: number | undefined

tests/cases/fourslash/fixExactOptionalUnassignableProperties6.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,12 @@
55
// @Filename: fixExactOptionalUnassignableProperties6.ts
66
// based on snapshotterInjected.ts in microsoft/playwright
77
//// type Data = {
8-
//// p?: (x: number) => void,
8+
//// f?: (x: number) => void,
9+
//// additional?: number,
10+
//// nop: string,
911
//// };
1012
//// declare function e(o: any): Data;
11-
//// e(101).p = undefined
13+
//// e(101).f = undefined
1214
verify.codeFixAvailable([
1315
{ description: ts.Diagnostics.Add_undefined_to_optional_property_type.message }
1416
]);
@@ -18,8 +20,10 @@ verify.codeFix({
1820
index: 0,
1921
newFileContent:
2022
`type Data = {
21-
p?: ((x: number) => void) | undefined,
23+
f?: ((x: number) => void) | undefined,
24+
additional?: number | undefined,
25+
nop: string,
2226
};
2327
declare function e(o: any): Data;
24-
e(101).p = undefined`,
28+
e(101).f = undefined`,
2529
});
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/// <reference path='fourslash.ts'/>
2+
3+
// @strictNullChecks: true
4+
// @exactOptionalPropertyTypes: true
5+
// @Filename: fixExactOptionalUnassignableProperties6.ts
6+
// based on snapshotterInjected.ts in microsoft/playwright
7+
//// type Data = {
8+
//// x?: {
9+
//// y?: number
10+
//// }
11+
//// }
12+
//// declare var d: Data
13+
//// d.x = { y: undefined }
14+
verify.codeFixAvailable([
15+
{ description: ts.Diagnostics.Add_undefined_to_optional_property_type.message }
16+
]);
17+
verify.codeFix({
18+
description: ts.Diagnostics.Add_undefined_to_optional_property_type.message,
19+
index: 0,
20+
newFileContent:
21+
`type Data = {
22+
x?: {
23+
y?: number | undefined
24+
}
25+
}
26+
declare var d: Data
27+
d.x = { y: undefined }`,
28+
});
29+

0 commit comments

Comments
 (0)