Skip to content

Commit 6a6c83c

Browse files
Revert "Revert "Add check for delete expression must be optional (#37921)" (#38154)" (#38173)
This reverts commit 1b8c68d.
1 parent 468ca9f commit 6a6c83c

14 files changed

+856
-4
lines changed

src/compiler/checker.ts

+13-2
Original file line numberDiff line numberDiff line change
@@ -27682,12 +27682,23 @@ namespace ts {
2768227682
}
2768327683
const links = getNodeLinks(expr);
2768427684
const symbol = getExportSymbolOfValueSymbolIfExported(links.resolvedSymbol);
27685-
if (symbol && isReadonlySymbol(symbol)) {
27686-
error(expr, Diagnostics.The_operand_of_a_delete_operator_cannot_be_a_read_only_property);
27685+
if (symbol) {
27686+
if (isReadonlySymbol(symbol)) {
27687+
error(expr, Diagnostics.The_operand_of_a_delete_operator_cannot_be_a_read_only_property);
27688+
}
27689+
27690+
checkDeleteExpressionMustBeOptional(expr, getTypeOfSymbol(symbol));
2768727691
}
2768827692
return booleanType;
2768927693
}
2769027694

27695+
function checkDeleteExpressionMustBeOptional(expr: AccessExpression, type: Type) {
27696+
const AnyOrUnknownOrNeverFlags = TypeFlags.AnyOrUnknown | TypeFlags.Never;
27697+
if (strictNullChecks && !(type.flags & AnyOrUnknownOrNeverFlags) && !(getFalsyFlags(type) & TypeFlags.Undefined)) {
27698+
error(expr, Diagnostics.The_operand_of_a_delete_operator_must_be_optional);
27699+
}
27700+
}
27701+
2769127702
function checkTypeOfExpression(node: TypeOfExpression): Type {
2769227703
checkExpression(node.expression);
2769327704
return typeofType;

src/compiler/diagnosticMessages.json

+4
Original file line numberDiff line numberDiff line change
@@ -2963,6 +2963,10 @@
29632963
"category": "Error",
29642964
"code": 2789
29652965
},
2966+
"The operand of a 'delete' operator must be optional.": {
2967+
"category": "Error",
2968+
"code": 2790
2969+
},
29662970

29672971
"Import declaration '{0}' is using private name '{1}'.": {
29682972
"category": "Error",

tests/baselines/reference/controlFlowDeleteOperator.errors.txt

+4-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1+
tests/cases/conformance/controlFlow/controlFlowDeleteOperator.ts(10,12): error TS2790: The operand of a 'delete' operator must be optional.
12
tests/cases/conformance/controlFlow/controlFlowDeleteOperator.ts(14,12): error TS2703: The operand of a 'delete' operator must be a property reference.
23

34

4-
==== tests/cases/conformance/controlFlow/controlFlowDeleteOperator.ts (1 errors) ====
5+
==== tests/cases/conformance/controlFlow/controlFlowDeleteOperator.ts (2 errors) ====
56
function f() {
67
let x: { a?: number | string, b: number | string } = { b: 1 };
78
x.a;
@@ -12,6 +13,8 @@ tests/cases/conformance/controlFlow/controlFlowDeleteOperator.ts(14,12): error T
1213
x.b;
1314
delete x.a;
1415
delete x.b;
16+
~~~
17+
!!! error TS2790: The operand of a 'delete' operator must be optional.
1518
x.a;
1619
x.b;
1720
x;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
tests/cases/conformance/expressions/optionalChaining/delete/deleteChain.ts(2,8): error TS2790: The operand of a 'delete' operator must be optional.
2+
tests/cases/conformance/expressions/optionalChaining/delete/deleteChain.ts(3,9): error TS2790: The operand of a 'delete' operator must be optional.
3+
tests/cases/conformance/expressions/optionalChaining/delete/deleteChain.ts(6,8): error TS2790: The operand of a 'delete' operator must be optional.
4+
tests/cases/conformance/expressions/optionalChaining/delete/deleteChain.ts(7,9): error TS2790: The operand of a 'delete' operator must be optional.
5+
tests/cases/conformance/expressions/optionalChaining/delete/deleteChain.ts(10,8): error TS2790: The operand of a 'delete' operator must be optional.
6+
tests/cases/conformance/expressions/optionalChaining/delete/deleteChain.ts(11,9): error TS2790: The operand of a 'delete' operator must be optional.
7+
tests/cases/conformance/expressions/optionalChaining/delete/deleteChain.ts(14,8): error TS2790: The operand of a 'delete' operator must be optional.
8+
tests/cases/conformance/expressions/optionalChaining/delete/deleteChain.ts(15,8): error TS2790: The operand of a 'delete' operator must be optional.
9+
tests/cases/conformance/expressions/optionalChaining/delete/deleteChain.ts(16,9): error TS2790: The operand of a 'delete' operator must be optional.
10+
tests/cases/conformance/expressions/optionalChaining/delete/deleteChain.ts(19,8): error TS2790: The operand of a 'delete' operator must be optional.
11+
tests/cases/conformance/expressions/optionalChaining/delete/deleteChain.ts(20,9): error TS2790: The operand of a 'delete' operator must be optional.
12+
tests/cases/conformance/expressions/optionalChaining/delete/deleteChain.ts(23,8): error TS2790: The operand of a 'delete' operator must be optional.
13+
tests/cases/conformance/expressions/optionalChaining/delete/deleteChain.ts(24,9): error TS2790: The operand of a 'delete' operator must be optional.
14+
15+
16+
==== tests/cases/conformance/expressions/optionalChaining/delete/deleteChain.ts (13 errors) ====
17+
declare const o1: undefined | { b: string };
18+
delete o1?.b;
19+
~~~~~
20+
!!! error TS2790: The operand of a 'delete' operator must be optional.
21+
delete (o1?.b);
22+
~~~~~
23+
!!! error TS2790: The operand of a 'delete' operator must be optional.
24+
25+
declare const o2: undefined | { b: { c: string } };
26+
delete o2?.b.c;
27+
~~~~~~~
28+
!!! error TS2790: The operand of a 'delete' operator must be optional.
29+
delete (o2?.b.c);
30+
~~~~~~~
31+
!!! error TS2790: The operand of a 'delete' operator must be optional.
32+
33+
declare const o3: { b: undefined | { c: string } };
34+
delete o3.b?.c;
35+
~~~~~~~
36+
!!! error TS2790: The operand of a 'delete' operator must be optional.
37+
delete (o3.b?.c);
38+
~~~~~~~
39+
!!! error TS2790: The operand of a 'delete' operator must be optional.
40+
41+
declare const o4: { b?: { c: { d?: { e: string } } } };
42+
delete o4.b?.c.d?.e;
43+
~~~~~~~~~~~~
44+
!!! error TS2790: The operand of a 'delete' operator must be optional.
45+
delete (o4.b?.c.d)?.e;
46+
~~~~~~~~~~~~~~
47+
!!! error TS2790: The operand of a 'delete' operator must be optional.
48+
delete (o4.b?.c.d?.e);
49+
~~~~~~~~~~~~
50+
!!! error TS2790: The operand of a 'delete' operator must be optional.
51+
52+
declare const o5: { b?(): { c: { d?: { e: string } } } };
53+
delete o5.b?.().c.d?.e;
54+
~~~~~~~~~~~~~~~
55+
!!! error TS2790: The operand of a 'delete' operator must be optional.
56+
delete (o5.b?.().c.d?.e);
57+
~~~~~~~~~~~~~~~
58+
!!! error TS2790: The operand of a 'delete' operator must be optional.
59+
60+
declare const o6: { b?: { c: { d?: { e: string } } } };
61+
delete o6.b?.['c'].d?.['e'];
62+
~~~~~~~~~~~~~~~~~~~~
63+
!!! error TS2790: The operand of a 'delete' operator must be optional.
64+
delete (o6.b?.['c'].d?.['e']);
65+
~~~~~~~~~~~~~~~~~~~~
66+
!!! error TS2790: The operand of a 'delete' operator must be optional.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
tests/cases/compiler/deleteExpressionMustBeOptional.ts(34,10): error TS2339: Property 'j' does not exist on type 'Foo'.
2+
3+
4+
==== tests/cases/compiler/deleteExpressionMustBeOptional.ts (1 errors) ====
5+
interface Foo {
6+
a: number
7+
b: number | undefined
8+
c: number | null
9+
d?: number
10+
e: number | undefined | null
11+
f?: number | undefined | null
12+
g: unknown
13+
h: any
14+
i: never
15+
}
16+
17+
interface AA {
18+
[s: string]: number
19+
}
20+
21+
type BB = {
22+
[P in keyof any]: number
23+
}
24+
25+
declare const f: Foo
26+
declare const a: AA
27+
declare const b: BB
28+
29+
delete f.a
30+
delete f.b
31+
delete f.c
32+
delete f.d
33+
delete f.e
34+
delete f.f
35+
delete f.g
36+
delete f.h
37+
delete f.i
38+
delete f.j
39+
~
40+
!!! error TS2339: Property 'j' does not exist on type 'Foo'.
41+
42+
delete a.a
43+
delete a.b
44+
45+
delete b.a
46+
delete b.b
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
//// [deleteExpressionMustBeOptional.ts]
2+
interface Foo {
3+
a: number
4+
b: number | undefined
5+
c: number | null
6+
d?: number
7+
e: number | undefined | null
8+
f?: number | undefined | null
9+
g: unknown
10+
h: any
11+
i: never
12+
}
13+
14+
interface AA {
15+
[s: string]: number
16+
}
17+
18+
type BB = {
19+
[P in keyof any]: number
20+
}
21+
22+
declare const f: Foo
23+
declare const a: AA
24+
declare const b: BB
25+
26+
delete f.a
27+
delete f.b
28+
delete f.c
29+
delete f.d
30+
delete f.e
31+
delete f.f
32+
delete f.g
33+
delete f.h
34+
delete f.i
35+
delete f.j
36+
37+
delete a.a
38+
delete a.b
39+
40+
delete b.a
41+
delete b.b
42+
43+
//// [deleteExpressionMustBeOptional.js]
44+
delete f.a;
45+
delete f.b;
46+
delete f.c;
47+
delete f.d;
48+
delete f.e;
49+
delete f.f;
50+
delete f.g;
51+
delete f.h;
52+
delete f.i;
53+
delete f.j;
54+
delete a.a;
55+
delete a.b;
56+
delete b.a;
57+
delete b.b;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
=== tests/cases/compiler/deleteExpressionMustBeOptional.ts ===
2+
interface Foo {
3+
>Foo : Symbol(Foo, Decl(deleteExpressionMustBeOptional.ts, 0, 0))
4+
5+
a: number
6+
>a : Symbol(Foo.a, Decl(deleteExpressionMustBeOptional.ts, 0, 15))
7+
8+
b: number | undefined
9+
>b : Symbol(Foo.b, Decl(deleteExpressionMustBeOptional.ts, 1, 13))
10+
11+
c: number | null
12+
>c : Symbol(Foo.c, Decl(deleteExpressionMustBeOptional.ts, 2, 25))
13+
14+
d?: number
15+
>d : Symbol(Foo.d, Decl(deleteExpressionMustBeOptional.ts, 3, 20))
16+
17+
e: number | undefined | null
18+
>e : Symbol(Foo.e, Decl(deleteExpressionMustBeOptional.ts, 4, 14))
19+
20+
f?: number | undefined | null
21+
>f : Symbol(Foo.f, Decl(deleteExpressionMustBeOptional.ts, 5, 32))
22+
23+
g: unknown
24+
>g : Symbol(Foo.g, Decl(deleteExpressionMustBeOptional.ts, 6, 33))
25+
26+
h: any
27+
>h : Symbol(Foo.h, Decl(deleteExpressionMustBeOptional.ts, 7, 14))
28+
29+
i: never
30+
>i : Symbol(Foo.i, Decl(deleteExpressionMustBeOptional.ts, 8, 10))
31+
}
32+
33+
interface AA {
34+
>AA : Symbol(AA, Decl(deleteExpressionMustBeOptional.ts, 10, 1))
35+
36+
[s: string]: number
37+
>s : Symbol(s, Decl(deleteExpressionMustBeOptional.ts, 13, 5))
38+
}
39+
40+
type BB = {
41+
>BB : Symbol(BB, Decl(deleteExpressionMustBeOptional.ts, 14, 1))
42+
43+
[P in keyof any]: number
44+
>P : Symbol(P, Decl(deleteExpressionMustBeOptional.ts, 17, 5))
45+
}
46+
47+
declare const f: Foo
48+
>f : Symbol(f, Decl(deleteExpressionMustBeOptional.ts, 20, 13))
49+
>Foo : Symbol(Foo, Decl(deleteExpressionMustBeOptional.ts, 0, 0))
50+
51+
declare const a: AA
52+
>a : Symbol(a, Decl(deleteExpressionMustBeOptional.ts, 21, 13))
53+
>AA : Symbol(AA, Decl(deleteExpressionMustBeOptional.ts, 10, 1))
54+
55+
declare const b: BB
56+
>b : Symbol(b, Decl(deleteExpressionMustBeOptional.ts, 22, 13))
57+
>BB : Symbol(BB, Decl(deleteExpressionMustBeOptional.ts, 14, 1))
58+
59+
delete f.a
60+
>f.a : Symbol(Foo.a, Decl(deleteExpressionMustBeOptional.ts, 0, 15))
61+
>f : Symbol(f, Decl(deleteExpressionMustBeOptional.ts, 20, 13))
62+
>a : Symbol(Foo.a, Decl(deleteExpressionMustBeOptional.ts, 0, 15))
63+
64+
delete f.b
65+
>f.b : Symbol(Foo.b, Decl(deleteExpressionMustBeOptional.ts, 1, 13))
66+
>f : Symbol(f, Decl(deleteExpressionMustBeOptional.ts, 20, 13))
67+
>b : Symbol(Foo.b, Decl(deleteExpressionMustBeOptional.ts, 1, 13))
68+
69+
delete f.c
70+
>f.c : Symbol(Foo.c, Decl(deleteExpressionMustBeOptional.ts, 2, 25))
71+
>f : Symbol(f, Decl(deleteExpressionMustBeOptional.ts, 20, 13))
72+
>c : Symbol(Foo.c, Decl(deleteExpressionMustBeOptional.ts, 2, 25))
73+
74+
delete f.d
75+
>f.d : Symbol(Foo.d, Decl(deleteExpressionMustBeOptional.ts, 3, 20))
76+
>f : Symbol(f, Decl(deleteExpressionMustBeOptional.ts, 20, 13))
77+
>d : Symbol(Foo.d, Decl(deleteExpressionMustBeOptional.ts, 3, 20))
78+
79+
delete f.e
80+
>f.e : Symbol(Foo.e, Decl(deleteExpressionMustBeOptional.ts, 4, 14))
81+
>f : Symbol(f, Decl(deleteExpressionMustBeOptional.ts, 20, 13))
82+
>e : Symbol(Foo.e, Decl(deleteExpressionMustBeOptional.ts, 4, 14))
83+
84+
delete f.f
85+
>f.f : Symbol(Foo.f, Decl(deleteExpressionMustBeOptional.ts, 5, 32))
86+
>f : Symbol(f, Decl(deleteExpressionMustBeOptional.ts, 20, 13))
87+
>f : Symbol(Foo.f, Decl(deleteExpressionMustBeOptional.ts, 5, 32))
88+
89+
delete f.g
90+
>f.g : Symbol(Foo.g, Decl(deleteExpressionMustBeOptional.ts, 6, 33))
91+
>f : Symbol(f, Decl(deleteExpressionMustBeOptional.ts, 20, 13))
92+
>g : Symbol(Foo.g, Decl(deleteExpressionMustBeOptional.ts, 6, 33))
93+
94+
delete f.h
95+
>f.h : Symbol(Foo.h, Decl(deleteExpressionMustBeOptional.ts, 7, 14))
96+
>f : Symbol(f, Decl(deleteExpressionMustBeOptional.ts, 20, 13))
97+
>h : Symbol(Foo.h, Decl(deleteExpressionMustBeOptional.ts, 7, 14))
98+
99+
delete f.i
100+
>f.i : Symbol(Foo.i, Decl(deleteExpressionMustBeOptional.ts, 8, 10))
101+
>f : Symbol(f, Decl(deleteExpressionMustBeOptional.ts, 20, 13))
102+
>i : Symbol(Foo.i, Decl(deleteExpressionMustBeOptional.ts, 8, 10))
103+
104+
delete f.j
105+
>f : Symbol(f, Decl(deleteExpressionMustBeOptional.ts, 20, 13))
106+
107+
delete a.a
108+
>a : Symbol(a, Decl(deleteExpressionMustBeOptional.ts, 21, 13))
109+
110+
delete a.b
111+
>a : Symbol(a, Decl(deleteExpressionMustBeOptional.ts, 21, 13))
112+
113+
delete b.a
114+
>b : Symbol(b, Decl(deleteExpressionMustBeOptional.ts, 22, 13))
115+
116+
delete b.b
117+
>b : Symbol(b, Decl(deleteExpressionMustBeOptional.ts, 22, 13))
118+

0 commit comments

Comments
 (0)