Skip to content

Commit f6b0fcb

Browse files
committed
add to README.
1 parent e729a67 commit f6b0fcb

File tree

3 files changed

+111
-29
lines changed

3 files changed

+111
-29
lines changed

README.md

Lines changed: 80 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ const optionalEmpty1: Optional<string> = Optional.empty(); // type hinting requi
4949
const optionalEmpty2 = Optional.empty<string>(); // or parameterize explicitly
5050
```
5151

52+
53+
5254
### operations
5355

5456
```ts
@@ -58,16 +60,17 @@ const optional: Optional<string> = Optional.ofNullable( /* some optional value:
5860
// this method is not used match.
5961
optional.get();
6062

61-
// be whether a payload is present or not.
63+
// represent whether this is present or not.
6264
optional.isPresent
6365

64-
// be whether this is empty or not. (negation of `isPresent` property)
66+
// represent whether this is empty or not. (negation of `isPresent` property)
6567
optional.isEmpty
6668

67-
// execute the given consumer if a payload is present.
69+
// if a payload is present, execute the given `consumer`.
6870
optional.ifPresent(value => console.log(value));
6971

70-
// execute the first argument (consumer) if a payload is present, execute the second argument (emptyAction) otherwise.
72+
// if a payload is present, execute the first argument (`consumer`),
73+
// otherwise execute the second argument (emptyAction).
7174
optional.ifPresentOrElse(value => console.log(value), () => console.log("empty"));
7275

7376
// filter a payload with additional predicate.
@@ -82,18 +85,87 @@ const powerIfPositive: (x: Number) => Optional<Number>
8285
const numberOptional: Optional<number> = Optional.ofNullable(/* some optional value: null | number */)
8386
numberOptional.flatMap(value => powerIfPositive(value));
8487

85-
// return this if this is present, return the given another optional otherwise.
88+
// if this is present, return this, otherwise return the given another optional.
8689
const another: Optional<string> = Optional.ofNullable(/* ... */);
8790
optional.or(another);
8891

89-
// retrieve a payload if this is present, return the given value otherwise.
92+
// if this is present retrieve the payload,
93+
// otherwise return the given value.
9094
optional.orElse("bar");
9195

92-
// retrieve a payload if this is present, return a value supplied by the given function otherwise.
96+
// if a payload is present, retrieve the payload,
97+
// otherwise return a value supplied by the given function.
9398
optional.orElseGet(() => "bar");
9499

95-
// retrieve a payload if this is present, throws an exception supplied by the given function otherwise.
100+
// if a payload is present, retrieve the payload,
101+
// otherwise throw an exception supplied by the given function.
96102
optional.orElseThrow(() => new Error());
103+
104+
// if a payload is present, retrieve the payload,
105+
// otherwise return null.
106+
optional.orNull();
107+
108+
// if a payload is present, retrieve the payload,
109+
// otherwise return undefined.
110+
optional.orUndefined();
111+
112+
// return an appropriate result by emulating pattern matching with the given cases.
113+
optional.matches({
114+
present: value => value.length,
115+
empty: () => 0,
116+
})
117+
118+
// convert this to an Option value.
119+
optional.toOption();
120+
```
121+
122+
### prototype-free types
123+
124+
While `Optional`'s fluent interface for method chaining with `prototype` is usually useful and elegant,
125+
relying on `prototype` can cause some problems in certain situations like an external function that copies such objects *except* `prototype`.
126+
For example, `setState` of React reflects the given value as a state except its `prototype` (and then you will see "TypeError: undefined is not a function" in runtime though TypeScript compilation has been succeeded!).
127+
128+
To avoid this issue, you have three options that *open* an `Optional` into a *prototype-free*, or a simple JavaScript object (associative array, string etc.).
129+
130+
- `Option.orNull`
131+
- `Option.orUndefined`
132+
- `Option.toOption`
133+
134+
#### `Option.orNull` and `Option.orUndefined`
135+
136+
Using `Option.orNull` or `Option.orUndefined` is the simple way to obtain prototype-free objects.
137+
These methods convert an Optional<T> into a value of *type union*.
138+
139+
`Option<T>.orNull` returns `T | null`.
140+
141+
`Optional<T>.orUndefined` returns `T | undefined`. The `T | undefined` type is compatible with [optional parameters and properties](http://www.typescriptlang.org/docs/handbook/advanced-types.html#optional-parameters-and-properties) of TypeScript.
142+
143+
Use `Optional.ofNullable` to restore an Optional value from a value of these type unions.
144+
145+
```ts
146+
const update: <T> (original: T) => T = /* some external function */
147+
const optional: Optional<string> = /* some Optional object */;
148+
149+
let nullable: string | null = optional.orNull();
150+
let orUndefined: string | undefined = optional.orUndefined();
151+
152+
// update using external functions!
153+
nullable = update(nullable);
154+
orUndefined = update(orUndefined);
155+
156+
// retore from (string | null).
157+
const optionalFromNullable: Optional<string> = Optional.ofNullble(nullable);
158+
159+
// restore from (string | undefined).
160+
const optionalFromOrUndefined: Optional<string> = Optional.ofNullble(orUndefined);
161+
```
162+
163+
#### `Option.toOption`
164+
165+
```ts
166+
const optional: Optional<string> = /* some Optional value */;
167+
const option: Option<string> = optional.toOption();
168+
const optionalFromOption: Optional<string> = Optional.from(option);
97169
```
98170

99171
## License

lib/index.ts

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,28 +17,32 @@ import { Option, Cases } from "./types";
1717
*/
1818
export default abstract class Optional<T> {
1919
/**
20-
* Returns `true` if this is present, otherwise `false`.
20+
* Represents whether this is present or not.
21+
*
22+
* If a payload is present, be `true` , otherwise be `false`.
2123
*/
2224
abstract get isPresent(): boolean;
2325

2426
/**
25-
* Returns true if this is empty, otherwise false.
26-
* This method is negation of `Optional.isPresent`.
27+
* Represents whether this is empty or not.
2728
*
29+
* If this is empty, be `true`, otherwise be `false`.
30+
* This method is negation of `Optional.isPresent`.
2831
*/
2932
get isEmpty(): boolean {
3033
return !this.isPresent;
3134
}
3235

3336
/**
37+
* Force to retrieve the payload.
3438
* If a payload is present, returns the payload, otherwise throws `TypeError`.
3539
*
3640
* @throws {TypeError} if this is empty.
3741
*/
3842
abstract get(): T;
3943

4044
/**
41-
* If a payload is present, executes the given `consumer`, otherwise not.
45+
* If a payload is present, executes the given `consumer`, otherwise does nothing.
4246
*
4347
* @param consumer a consumer of the payload
4448
*/
@@ -54,6 +58,8 @@ export default abstract class Optional<T> {
5458
abstract ifPresentOrElse(consumer: (value: T) => void, emptyAction: () => void): void;
5559

5660
/**
61+
* Filters a payload with an additional `predicate`.
62+
*
5763
* If a payload is present and the payload matches the given `predicate`, returns `this`,
5864
* otherwise returns an empty `Optional` even if this is present.
5965
*
@@ -62,6 +68,8 @@ export default abstract class Optional<T> {
6268
abstract filter(predicate: (value: T) => boolean): Optional<T>;
6369

6470
/**
71+
* Maps a payload with a mapper.
72+
*
6573
* If a payload is present, returns an `Optional` as if applying `Optional.ofNullable` to the result of
6674
* applying the given `mapper` to the payload,
6775
* otherwise returns an empty `Optional`.
@@ -71,6 +79,8 @@ export default abstract class Optional<T> {
7179
abstract map<U> (mapper: (value: T) => U): Optional<U>;
7280

7381
/**
82+
* Maps a payload with a mapper which returns Optional as a result.
83+
*
7484
* If a payload is present, returns the result of applying the given `mapper` to the payload,
7585
* otherwise returns an empty `Optional`.
7686
*
@@ -110,11 +120,6 @@ export default abstract class Optional<T> {
110120
*/
111121
abstract orElseThrow<U>(errorSupplier: () => U): T;
112122

113-
/**
114-
* Converts this to an `Option`.
115-
*/
116-
abstract toOption(): Option<T>;
117-
118123
/**
119124
* If a payload is present, returns the payload,
120125
* otherwise returns `null`.
@@ -127,6 +132,11 @@ export default abstract class Optional<T> {
127132
*/
128133
abstract orUndefined(): T | undefined;
129134

135+
/**
136+
* Converts this to an `Option`.
137+
*/
138+
abstract toOption(): Option<T>;
139+
130140
/**
131141
* Returns an appropriate result by emulating pattern matching with the given `cases`.
132142
* If a payload is present, returns the result of `present` case,

test/OptionalTest.ts

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -312,18 +312,6 @@ describe("Optional", () => {
312312
});
313313
});
314314

315-
describe("#toOption", () => {
316-
it("returns a Some value when it is present.", () => {
317-
let actual = sutPresent.toOption();
318-
assert.equal(actual.kind, "present");
319-
});
320-
321-
it("returns a None value when it is empty.", () => {
322-
let actual = sutEmpty.toOption();
323-
assert.equal(actual.kind, "empty");
324-
});
325-
});
326-
327315
describe("#orNull", () => {
328316
it("returns the original payload when it is present.", () => {
329317
let actual = sutPresent.orNull();
@@ -348,6 +336,18 @@ describe("Optional", () => {
348336
});
349337
});
350338

339+
describe("#toOption", () => {
340+
it("returns a Some value when it is present.", () => {
341+
let actual = sutPresent.toOption();
342+
assert.equal(actual.kind, "present");
343+
});
344+
345+
it("returns a None value when it is empty.", () => {
346+
let actual = sutEmpty.toOption();
347+
assert.equal(actual.kind, "empty");
348+
});
349+
});
350+
351351
describe("#matches", () => {
352352
let cases: Cases<string, number> = {
353353
present: x => x.length,

0 commit comments

Comments
 (0)