Skip to content

Commit 83ecaca

Browse files
committed
deprecate no-unsafe-optional-property-assignment. Fix #83
1 parent f441dc8 commit 83ecaca

File tree

6 files changed

+8
-133
lines changed

6 files changed

+8
-133
lines changed

README.md

Lines changed: 4 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,6 @@ Alternatively you can configure individual rules separately (see below).
7272
| no-unsafe-type-assertion ||| |
7373
| no-unsafe-readonly-mutable-assignment ||| |
7474
| no-unsafe-mutable-readonly-assignment | || [Not yet](https://github.com/danielnixon/eslint-plugin-total-functions/issues/99) |
75-
| no-unsafe-optional-property-assignment | [Not yet](https://github.com/danielnixon/eslint-plugin-total-functions/issues/83) || |
7675
| no-unsafe-enum-assignment ||| |
7776
| no-enums ||| |
7877
| no-partial-url-constructor ||| |
@@ -83,6 +82,10 @@ Alternatively you can configure individual rules separately (see below).
8382
| no-partial-array-reduce ||| |
8483
| no-hidden-type-assertions | || |
8584

85+
### Deprecated rules
86+
87+
* no-unsafe-optional-property-assignment
88+
8689
### total-functions/require-strict-mode
8790

8891
The world is a very strange place when [strict mode](https://www.typescriptlang.org/tsconfig#strict) is disabled. This rule enforces strict mode and [noUncheckedIndexedAccess](https://devblogs.microsoft.com/typescript/announcing-typescript-4-1-beta/#no-unchecked-indexed-access) mode (which is sadly not included under the strict umbrella).
@@ -143,31 +146,6 @@ The solution is to append `as const` to the RHS:
143146

144147
For examples of assignment that this rule considers valid and invalid, see [no-unsafe-mutable-readonly-assignment.test.ts](https://github.com/danielnixon/eslint-plugin-total-functions/blob/master/src/rules/no-unsafe-mutable-readonly-assignment.test.ts).
145148

146-
### total-functions/no-unsafe-optional-property-assignment
147-
148-
Optional properties (those with a `?` after their name) interact badly with TypeScript's structural type system in a way that can lead to unsoundness. Example:
149-
150-
```ts
151-
type Foo = { readonly foo: string };
152-
type Bar = Foo & { readonly bar?: () => unknown };
153-
154-
const thing = { foo: "foo", bar: "bar" };
155-
const foo: Foo = thing;
156-
const bar: Bar = foo;
157-
158-
if (bar.bar !== undefined) {
159-
bar.bar(); // explodes at runtime
160-
}
161-
```
162-
163-
I find this scenario particularly vexing because it doesn't require type assertions, or plain JS with incorrect \*.d.ts typings, or anything 'loose' like that. You can pull it off with otherwise nicely typed, functional TypeScript (strict mode enabled, no interfaces, no classes, everything readonly, everything const, no type assertions, no plain JS, etc).
164-
165-
This rule bans assignment from one type to another, if:
166-
1. the destination type has an optional property, and
167-
2. the source type has no matching property (either optional or otherwise).
168-
169-
This rule is excluded from the `recommended` config until [#83](https://github.com/danielnixon/eslint-plugin-total-functions/issues/83) lands.
170-
171149
### total-functions/no-enums
172150

173151
Enums have a number of issues, including unsoundness issues (which are especially relevant here). This rule bans the declaration of enums entirely. Use an alternative such as a union of strings instead.

src/configs/all.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ export = {
44
"total-functions/no-unsafe-type-assertion": "error",
55
"total-functions/no-unsafe-readonly-mutable-assignment": "error",
66
"total-functions/no-unsafe-mutable-readonly-assignment": "error",
7-
"total-functions/no-unsafe-optional-property-assignment": "error",
87
"total-functions/require-strict-mode": "error",
98
"total-functions/no-unsafe-enum-assignment": "error",
109
"total-functions/no-enums": "error",

src/configs/recommended.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@ export = {
33
rules: {
44
"total-functions/no-unsafe-type-assertion": "error",
55
"total-functions/no-unsafe-readonly-mutable-assignment": "error",
6-
// TODO this is too noisy until https://github.com/danielnixon/eslint-plugin-total-functions/issues/83 lands
7-
// "total-functions/no-unsafe-optional-property-assignment": "error",
86
"total-functions/require-strict-mode": "error",
97
"total-functions/no-unsafe-enum-assignment": "error",
108
"total-functions/no-enums": "error",

src/rules/index.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import noUnsafeTypeAssertion from "./no-unsafe-type-assertion";
22
import noUnsafeReadonlyMutableAssignment from "./no-unsafe-readonly-mutable-assignment";
33
import noUnsafeMutableReadonlyAssignment from "./no-unsafe-mutable-readonly-assignment";
4-
import noUnsafeOptionalPropertyAssignment from "./no-unsafe-optional-property-assignment";
54
import requireStrictMode from "./require-strict-mode";
65
import noUnsafeEnumAssignment from "./no-unsafe-enum-assignment";
76
import noEnums from "./no-enums";
@@ -18,7 +17,6 @@ export default {
1817
"no-unsafe-type-assertion": noUnsafeTypeAssertion,
1918
"no-unsafe-readonly-mutable-assignment": noUnsafeReadonlyMutableAssignment,
2019
"no-unsafe-mutable-readonly-assignment": noUnsafeMutableReadonlyAssignment,
21-
"no-unsafe-optional-property-assignment": noUnsafeOptionalPropertyAssignment,
2220
"no-unsafe-enum-assignment": noUnsafeEnumAssignment,
2321
"no-enums": noEnums,
2422
"no-partial-url-constructor": noPartialUrlConstructor,

src/rules/no-unsafe-optional-property-assignment.test.ts

Lines changed: 0 additions & 62 deletions
This file was deleted.

src/rules/no-unsafe-optional-property-assignment.ts

Lines changed: 4 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,4 @@
1-
import { isSymbolFlagSet } from "tsutils";
2-
import { Symbol, SymbolFlags } from "typescript";
31
import { createRule } from "./common";
4-
import {
5-
createNoUnsafeAssignmentRule,
6-
UnsafeIndexAssignmentFunc,
7-
UnsafePropertyAssignmentFunc,
8-
} from "./unsafe-assignment-rule";
9-
10-
// eslint-disable-next-line functional/functional-parameters
11-
const unsafeIndexAssignmentFunc: UnsafeIndexAssignmentFunc = (): boolean =>
12-
false;
13-
14-
const unsafePropertyAssignmentFunc: UnsafePropertyAssignmentFunc = (
15-
// eslint-disable-next-line functional/prefer-immutable-types, @typescript-eslint/ban-types
16-
destinationProperty: Symbol,
17-
// eslint-disable-next-line functional/prefer-immutable-types, @typescript-eslint/ban-types
18-
sourceProperty: Symbol | undefined
19-
): boolean => {
20-
const destinationPropIsOptional = isSymbolFlagSet(
21-
destinationProperty,
22-
SymbolFlags.Optional
23-
);
24-
25-
return destinationPropIsOptional && sourceProperty === undefined;
26-
};
27-
28-
const message =
29-
"Assigning to an optional property when there is no such property in the source type (optional or otherwise) is unsafe.";
302

313
/**
324
* An ESLint rule to ban unsafe assignment to optional properties.
@@ -40,20 +12,12 @@ const noUnsafeOptionalPropertyAssignment = createRule({
4012
description: "Bans unsafe assignment to optional properties.",
4113
recommended: "error",
4214
},
43-
messages: {
44-
errorStringCallExpression: message,
45-
errorStringAssignmentExpression: message,
46-
errorStringVariableDeclaration: message,
47-
errorStringArrowFunctionExpression: message,
48-
errorStringTSAsExpression: message,
49-
errorStringTSTypeAssertion: message,
50-
},
15+
messages: {},
5116
schema: [],
17+
deprecated: true,
5218
},
53-
create: createNoUnsafeAssignmentRule(
54-
unsafePropertyAssignmentFunc,
55-
unsafeIndexAssignmentFunc
56-
),
19+
// eslint-disable-next-line functional/functional-parameters
20+
create: () => ({}),
5721
defaultOptions: [],
5822
} as const);
5923

0 commit comments

Comments
 (0)