Skip to content

Commit

Permalink
feat(optional): add support for type-guards to filter
Browse files Browse the repository at this point in the history
The `filter` method now supports type guards. This allows you to narrow the type contained by the
Optional from `Optional<A>` to `Optional<B>` where `B extends A`
  • Loading branch information
dancrumb committed Jul 31, 2024
1 parent c2ab00b commit 5b8ef88
Show file tree
Hide file tree
Showing 5 changed files with 23 additions and 2 deletions.
Binary file not shown.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
"git-cz": "4.9.0",
"prettier": "3.2.5",
"ts-node": "10.9.2",
"tsafe": "^1.7.2",
"typedoc": "^0.25.13",
"typedoc-plugin-missing-exports": "^2.2.0",
"typescript": "^5.4.5",
Expand Down
12 changes: 11 additions & 1 deletion src/Optional.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { expect, describe, test, vi } from 'vitest';

import {assert, Equals} from "tsafe";
import { Optional } from './Optional.js';
import { NoSuchElementException } from './exceptions/NoSuchElementException.js';

Expand Down Expand Up @@ -119,6 +119,16 @@ describe('Optional', () => {
.isPresent()
).toBe(false);
});
test('filters to a new type if given a type guard', () => {
type A = { x: number};
type B = A & { z: string};
const a: A = { x: 1};
const oA = Optional.of(a);
const filtered = oA.filter((x): x is B => 'z' in x);

assert<Equals<typeof filtered, Optional<B>>>(true);

});
});
describe('#toJSON', () => {
test('returns undefined when JSON stringified', () => {
Expand Down
4 changes: 3 additions & 1 deletion src/Optional.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ export class Optional<T> {
return new Optional<NonNullable<NT>>(value as NonNullable<NT>);
}

filter<S extends T>(predicate: (v: T) => v is S): Optional<S>;
filter(predicate: (v: T) => boolean): Optional<T>
/**
* Checks the value of the optional. If it matches the `predicate`, then a non-empty
* Optional of the same value will be returned.
Expand All @@ -53,7 +55,7 @@ export class Optional<T> {
*
* @param predicate
*/
filter(predicate: (v: T) => boolean): Optional<T> {
filter<S extends T>(predicate: (v: T) => v is S): Optional<S> {
if (haveValue(this.value) && predicate(this.value)) {
return new Optional(this.value);
}
Expand Down
8 changes: 8 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -442,6 +442,7 @@ __metadata:
git-cz: 4.9.0
prettier: 3.2.5
ts-node: 10.9.2
tsafe: ^1.7.2
typedoc: ^0.25.13
typedoc-plugin-missing-exports: ^2.2.0
typescript: ^5.4.5
Expand Down Expand Up @@ -6139,6 +6140,13 @@ __metadata:
languageName: node
linkType: hard

"tsafe@npm:^1.7.2":
version: 1.7.2
resolution: "tsafe@npm:1.7.2"
checksum: a02da1bb81fed3c35b1ce1ea5f6118a632c436d41536ca7a8a399d5890add53a0352da6f48b5fbb66d9f80c66c71de45de6071f696781fa03e738058d112ee80
languageName: node
linkType: hard

"tslib@npm:^2.1.0":
version: 2.6.2
resolution: "tslib@npm:2.6.2"
Expand Down

0 comments on commit 5b8ef88

Please sign in to comment.