Skip to content

Commit a728852

Browse files
committed
Support variable declarations, property assignments, destructuring
As long as it's not nested
1 parent 671096b commit a728852

File tree

2 files changed

+79
-24
lines changed

2 files changed

+79
-24
lines changed

src/services/codefixes/addOptionalPropertyUndefined.ts

Lines changed: 31 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -44,46 +44,55 @@ namespace ts.codefix {
4444
},
4545
});
4646

47-
function getTarget(file: SourceFile, pos: number) {
47+
// The target of the incorrect assignment
48+
// eg
49+
// this.definite = 1; -OR- definite = source
50+
// ^^^^ ^^^^^^^^
51+
// TODO: More examples here
52+
function getTarget(file: SourceFile, pos: number): MemberName | PropertyAccessExpression | undefined {
4853
const start = getTokenAtPosition(file, pos)
49-
if (isPropertyAccessExpression(start.parent) && start.parent.expression === start) {
50-
return start.parent
51-
}
52-
else if (isIdentifier(start) || isPrivateIdentifier(start)) {
53-
return start
54-
}
55-
return undefined
54+
return isPropertyAccessExpression(start.parent) && start.parent.expression === start ? start.parent
55+
: isIdentifier(start) || isPrivateIdentifier(start) ? start
56+
: undefined;
5657
}
5758

58-
function getSourceTarget(file: SourceFile, pos: number, checker: TypeChecker) {
59-
const target = getTarget(file, pos)
59+
function getSourceTarget(target: Node | undefined, checker: TypeChecker): { source: Node, target: Node } | undefined {
6060
if (!target) return undefined
6161
if (isBinaryExpression(target.parent) && target.parent.operatorToken.kind === SyntaxKind.EqualsToken) {
62-
return [target.parent.right, target]
62+
return { source: target.parent.right, target: target.parent.left }
63+
}
64+
else if (isVariableDeclaration(target.parent) && target.parent.initializer) {
65+
return { source: target.parent.initializer, target: target.parent.name }
6366
}
6467
else if (isCallExpression(target.parent)) {
6568
const n = checker.getSymbolAtLocation(target.parent.expression)
6669
if (!n?.valueDeclaration) return undefined
67-
if (!isIdentifier(target)) return undefined;
70+
if (!isExpression(target)) return undefined;
6871
const i = target.parent.arguments.indexOf(target)
6972
const name = (n.valueDeclaration as any as SignatureDeclaration).parameters[i].name
70-
if (isIdentifier(name)) return [target, name]
73+
if (isIdentifier(name)) return { source: target, target: name }
74+
}
75+
else if (isPropertyAssignment(target.parent) && isIdentifier(target.parent.name) ||
76+
isShorthandPropertyAssignment(target.parent)) {
77+
const parentTarget = getSourceTarget(target.parent.parent, checker)
78+
if (!parentTarget) return undefined
79+
const prop = checker.getPropertyOfType(checker.getTypeAtLocation(parentTarget.target), (target.parent.name as Identifier).text)
80+
const declaration = prop?.declarations?.[0]
81+
if (!declaration) return undefined
82+
return {
83+
source: isPropertyAssignment(target.parent) ? target.parent.initializer : target.parent.name,
84+
target: declaration
85+
}
7186
}
72-
// It's possible to handle destructuring by recording the path up through the structure
73-
// and following the reverse path down through the right-hand-side, but that's not worth the effort
7487
return undefined
7588
}
7689

7790
function getInfo(file: SourceFile, pos: number, checker: TypeChecker): Symbol[] {
78-
// The target of the incorrect assignment
79-
// eg
80-
// this.definite = 1; -OR- definite = source
81-
// ^^^^ ^^^^^^^^
82-
const sourceTarget = getSourceTarget(file, pos, checker)
91+
const sourceTarget = getSourceTarget(getTarget(file, pos), checker)
8392
if (!sourceTarget) return []
84-
const [sourceNode, targetNode] = sourceTarget
93+
const { source: sourceNode, target: targetNode } = sourceTarget
8594
const target = checker.getTypeAtLocation(targetNode)
86-
if (target.symbol?.declarations?.some(d => getSourceFileOfNode(d).fileName.match(/(node_modules|^lib)/))) return [];
95+
if (target.symbol?.declarations?.some(d => getSourceFileOfNode(d).fileName.match(/(node_modules|^lib\.)/))) return [];
8796
return checker.getExactOptionalUnassignableProperties(checker.getTypeAtLocation(sourceNode), target)
8897
}
8998

tests/cases/fourslash/fixExactOptionalUnassignableProperties2.ts

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,22 @@
2424
//// interface ID {
2525
//// a?: number
2626
//// }
27+
//// interface More {
28+
//// a?: number
29+
//// b?: number
30+
//// }
31+
//// interface Assignment {
32+
//// a?: number
33+
//// }
34+
//// interface PropertyAssignment {
35+
//// a?: number
36+
//// }
37+
//// interface ShorthandPropertyAssignment {
38+
//// a?: number
39+
//// }
40+
//// interface FPA {
41+
//// a?: number
42+
//// }
2743
//// interface J {
2844
//// a?: number | undefined
2945
//// }
@@ -54,6 +70,13 @@
5470
//// }
5571
//// declare var pwpd: PartialWrongPropertyDescriptor
5672
//// pd = pwpd
73+
//// declare var more: More
74+
//// more = j
75+
//// var assignment: Assignment = j
76+
//// var opa: { pa: PropertyAssignment } = { pa: j }
77+
//// var ospa: { j: ShorthandPropertyAssignment } = { j }
78+
//// declare function fpa(fpa: { fpa: FPA }): void
79+
//// fpa({ fpa: j })
5780

5881
verify.codeFixAll({
5982
fixId: "addOptionalPropertyUndefined",
@@ -78,7 +101,23 @@ interface ICP {
78101
a?: number | undefined
79102
}
80103
interface ID {
81-
a?: number
104+
a?: number | undefined
105+
}
106+
interface More {
107+
a?: number | undefined
108+
b?: number
109+
}
110+
interface Assignment {
111+
a?: number | undefined
112+
}
113+
interface PropertyAssignment {
114+
a?: number | undefined
115+
}
116+
interface ShorthandPropertyAssignment {
117+
a?: number | undefined
118+
}
119+
interface FPA {
120+
a?: number | undefined
82121
}
83122
interface J {
84123
a?: number | undefined
@@ -109,6 +148,13 @@ interface PartialWrongPropertyDescriptor {
109148
configurable?: boolean | undefined
110149
}
111150
declare var pwpd: PartialWrongPropertyDescriptor
112-
pd = pwpd`,
151+
pd = pwpd
152+
declare var more: More
153+
more = j
154+
var assignment: Assignment = j
155+
var opa: { pa: PropertyAssignment } = { pa: j }
156+
var ospa: { j: ShorthandPropertyAssignment } = { j }
157+
declare function fpa(fpa: { fpa: FPA }): void
158+
fpa({ fpa: j })`,
113159
});
114160

0 commit comments

Comments
 (0)