Skip to content

Commit

Permalink
Move the rejected promises part of no-throw to new rule no-reject (#118)
Browse files Browse the repository at this point in the history
* Add the no-reject rule

* Revert changes to the readme

* Revert "update `no-throw` to not allow Promise.reject() (#116)"

This reverts commit 4efdf16.

* Add docs

* Remove previously removed line from readme
  • Loading branch information
jonaskello authored Feb 19, 2019
1 parent f40c59f commit 704fb3a
Show file tree
Hide file tree
Showing 6 changed files with 83 additions and 32 deletions.
31 changes: 26 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ In addition to immutable rules this project also contains a few rules for enforc
* [no-loop-statement](#no-loop-statement)
* [no-throw](#no-throw)
* [no-try](#no-try)
* [no-reject](#no-reject)
* [Recommended built-in rules](#recommended-built-in-rules)

## Immutability rules
Expand Down Expand Up @@ -451,7 +452,25 @@ function divide(x: number, y: number): number | Error {
}
```

And likewise for async functions:
Or in the case of an async function, a rejected promise should be returned.

```typescript
async function divide(x: Promise<number>, y: Promise<number>): Promise<number> {
const [xv, yv] = await Promise.all([x, y]);

return yv === 0
? Promise.reject(new Error("Cannot divide by zero."))
: xv / yv;
}
```

### no-try

Try statements are not part of functional programming. See [no-throw](#no-throw) for more information.

### no-reject

You can view a `Promise` as a result object with built-in error (something like `{ value: number } | { error: Error }`) in which case a rejected `Promise` can be viewed a returned result and thus fits with functional programming. You can also view a rejected promise as something similar to an exception and as such something that does not fit with functional programming. If your view is the latter you can use the `no-reject` rule to disallow rejected promises.

```typescript
async function divide(
Expand All @@ -460,14 +479,16 @@ async function divide(
): Promise<number | Error> {
const [xv, yv] = await Promise.all([x, y]);

// Rejecting the promise is not allowed so resolve to an Error instead

// return yv === 0
// ? Promise.reject(new Error("Cannot divide by zero."))
// : xv / yv;

return yv === 0 ? new Error("Cannot divide by zero.") : xv / yv;
}
```

### no-try

Try statements are not part of functional programming. See [no-throw](#no-throw) for more information.

## Options

### Using the `ignore-local` option
Expand Down
30 changes: 30 additions & 0 deletions src/noRejectRule.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import * as ts from "typescript";
import * as Lint from "tslint";
import {
createInvalidNode,
CheckNodeResult,
createCheckNodeRule
} from "./shared/check-node";

type Options = {};

// tslint:disable-next-line:variable-name
export const Rule = createCheckNodeRule(
checkNode,
"Unexpected reject, return an error instead."
);

function checkNode(
node: ts.Node,
_ctx: Lint.WalkContext<Options>
): CheckNodeResult {
if (
ts.isPropertyAccessExpression(node) &&
ts.isIdentifier(node.expression) &&
node.expression.text === "Promise" &&
node.name.text === "reject"
) {
return { invalidNodes: [createInvalidNode(node, [])] };
}
return { invalidNodes: [] };
}
15 changes: 3 additions & 12 deletions src/noThrowRule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,7 @@ function checkNode(
node: ts.Node,
_ctx: Lint.WalkContext<Options>
): CheckNodeResult {
if (utils.isThrowStatement(node)) {
return { invalidNodes: [createInvalidNode(node, [])] };
}
if (
ts.isPropertyAccessExpression(node) &&
ts.isIdentifier(node.expression) &&
node.expression.text === "Promise" &&
node.name.text === "reject"
) {
return { invalidNodes: [createInvalidNode(node, [])] };
}
return { invalidNodes: [] };
return utils.isThrowStatement(node)
? { invalidNodes: [createInvalidNode(node, [])] }
: { invalidNodes: [] };
}
18 changes: 18 additions & 0 deletions test/rules/no-reject/default/test.ts.lint
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
const error = new Error();

function foo(): Promise<number> {
if (Math.random() > 0.5) {
return Promise.reject(new Error("bar"))
~~~~~~~~~~~~~~ [failure]
}
return Promise.resolve(10)
}

function bar(): Promise<number | Error> {
if (Math.random() > 0.5) {
return Promise.resolve(new Error("foo"))
}
return Promise.resolve(10)
}

[failure]: Unexpected reject, return an error instead.
6 changes: 6 additions & 0 deletions test/rules/no-reject/default/tslint.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"rulesDirectory": ["../../../../rules"],
"rules": {
"no-reject": true
}
}
15 changes: 0 additions & 15 deletions test/rules/no-throw/default/test.ts.lint
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,4 @@ throw error;
throw new Error();
~~~~~~~~~~~~~~~~~~ [failure]

function foo(): Promise<number> {
if (Math.random() > 0.5) {
return Promise.reject(new Error("bar"))
~~~~~~~~~~~~~~ [failure]
}
return Promise.resolve(10)
}

function bar(): Promise<number | Error> {
if (Math.random() > 0.5) {
return Promise.resolve(new Error("foo"))
}
return Promise.resolve(10)
}

[failure]: Unexpected throw, throwing exceptions is not functional.

0 comments on commit 704fb3a

Please sign in to comment.