Skip to content

Commit 59d6ee4

Browse files
committed
[doc] Improve examples
1 parent 70500f1 commit 59d6ee4

File tree

6 files changed

+65
-53
lines changed

6 files changed

+65
-53
lines changed

README.md

+3-3
Original file line numberDiff line numberDiff line change
@@ -239,8 +239,8 @@ Then we add a first `with` clause:
239239

240240
```ts
241241
.with([{ status: 'loading' }, { type: 'success' }], ([state, event]) => ({
242-
// `state` is infered as { status: 'loading' }
243-
// `event` is infered as { type: 'success', data: string }
242+
// `state` is inferred as { status: 'loading' }
243+
// `event` is inferred as { type: 'success', data: string }
244244
status: 'success',
245245
data: event.data,
246246
}))
@@ -964,7 +964,7 @@ const output = match({ score: 10 })
964964
{
965965
score: P.when((score): score is 5 => score === 5),
966966
},
967-
(input) => '😐' // input is infered as { score: 5 }
967+
(input) => '😐' // input is inferred as { score: 5 }
968968
)
969969
.with({ score: P.when((score) => score < 5) }, () => '😞')
970970
.with({ score: P.when((score) => score > 5) }, () => '🙂')

ROADMAP.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
### Roadmap
22

3-
- [ ] add a `rest` (maybe `rest(Pattern<a>)`) pattern for list. Example of using `rest`:
3+
- [ ] add a `P.rest` (maybe `P.rest(Pattern<a>)`) pattern for list. Example of using `P.rest`:
44

55
```ts
66
const reverse = <T>(xs: T[]): T[] => {
77
return (
88
match<T[], T[]>(xs)
99
// matches a list with at least one element
10-
.with([__, ...rest(__)], ([x, ...xs]) => [...reverse(xs), x])
10+
.with([__, ...P.rest(__)], ([x, ...xs]) => [...reverse(xs), x])
1111
.otherwise(() => [])
1212
);
1313
};

examples/one-file-demo.ts

+57-45
Original file line numberDiff line numberDiff line change
@@ -1,75 +1,82 @@
11
/**
2-
* Demo summary:
3-
* - Basic pattern with inference with a wildcard
4-
* -> use case: better than switch because you can use it in a react component
5-
* - using select
6-
* -> easy to extract a value from the input Data Structure
7-
* - using P.union
8-
* -> matching several cases
9-
* - basic exhaustiveness checking
10-
* -> checking if everything is handled
11-
* - complexe exhaustiveness checking: matching on several values with a tuple
12-
* -> matching all combination of 2 enums
13-
* - P.array, P.optional, isMatching: validating input
14-
* -> typing API responses from 3rd party backends which aren't versioned
2+
* ### One file TS-Pattern demo.
3+
*
4+
* This demo will demonstrate:
5+
* - How to use pattern matching and wildcards
6+
* - How to extract a value from the input Data Structure using `P.select`
7+
* - How to match several cases using `P.union`
8+
* - How to leverage exhaustiveness checking to make sure every case is handled
9+
* - How to pattern match on several values at once using tuples
10+
* - How to validate an unknown API responses using `P.array`, `P.optional` and `isMatching`
1511
*/
1612

1713
import { isMatching, match, P, __ } from '../src';
1814

19-
/**
20-
* Use case 1: handling discriminated union types
21-
*/
15+
/**************************************************
16+
* Use case 1: handling discriminated union types *
17+
**************************************************/
2218

2319
type Response =
24-
| { type: 'video'; format: 'mp4' | 'webm'; src: string }
25-
| { type: 'image'; extension: 'gif' | 'jpg' | 'png'; src: string }
26-
| { type: 'text'; content: string; tags: { name: string; id: number }[] };
20+
| { type: 'video'; data: { format: 'mp4' | 'webm'; src: string } }
21+
| { type: 'image'; data: { extension: 'gif' | 'jpg' | 'png'; src: string } }
22+
| { type: 'text'; data: string; tags: { name: string; id: number }[] };
2723

28-
const exampleFunction1 = (input: Response) =>
24+
const exampleFunction1 = (input: Response): string =>
2925
match(input)
3026
// 1. Basic pattern with inference with a wildcard
31-
.with({ type: 'video', format: 'mp4' }, (video) => video.src)
27+
.with({ type: 'video', data: { format: 'mp4' } }, (video) => video.data.src)
3228
// 2. using select
3329
.with(
34-
{ type: 'image', extension: 'gif', src: P.select() },
30+
{ type: 'image', data: { extension: 'gif', src: P.select() } },
3531
(value) => value + '!!'
3632
)
3733
// 3. using P.union
3834
.with(
39-
{ type: 'image', extension: P.union('jpg', 'png'), src: P.select() },
35+
{
36+
type: 'image',
37+
data: { extension: P.union('jpg', 'png'), src: P.select() },
38+
},
4039
(value) => value + '!!'
4140
)
42-
// 4. basic exhaustiveness checking
43-
// @ts-expect-error
41+
// 4. selecting all tag names with P.array and P.select
42+
.with(
43+
{ type: 'text', tags: P.array({ name: P.select() }) },
44+
(tagNames) => tagNames.join(', ') + '!!'
45+
)
46+
// 5. basic exhaustiveness checking
47+
// @ts-expect-error: { type: 'video', data: { format: 'webm' } } isn't covered
4448
.exhaustive();
4549

46-
/**
47-
* Use case 2: multi params conditional logic
48-
*/
50+
/**********************************************
51+
* Use case 2: multi params conditional logic *
52+
**********************************************/
4953

50-
type UserType = 'contributor' | 'spectator';
54+
type UserType = 'editor' | 'viewer';
55+
// Uncomment 'premium' to see exhaustive checking in action
5156
type OrgPlan = 'basic' | 'pro' /* | 'premium' */ | 'enterprise';
5257

53-
const exampleFunction2 = (org: OrgPlan, user: UserType) =>
54-
// Checking several enums with tuples
58+
const exampleFunction2 = (org: OrgPlan, user: UserType): string =>
59+
// 1. Checking several enums with tuples
5560
match([org, user] as const)
5661
.with(['basic', __], () => `Please upgrade to unlock this feature!`)
62+
// 2. `.with()` can take several patterns. It will match if one of them do.
5763
.with(
58-
[P.union('pro', 'enterprise'), 'spectator'],
64+
['pro', 'viewer'],
65+
['enterprise', 'viewer'],
5966
() => `Your account doesn't have permissions to use this feature`
6067
)
61-
.with(['pro', 'contributor'], () => `Hello!`)
62-
.with(['enterprise', 'contributor'], () => `You are our favorite customer!`)
63-
// 5. complex exhaustive checking
68+
.with(['pro', 'editor'], () => `Hello!`)
69+
.with(['enterprise', 'editor'], () => `You are our favorite customer!`)
70+
// 3. complex exhaustive checking
6471
.exhaustive();
6572

66-
/**
67-
* Use case 3: Validation an API response
68-
*/
73+
/******************************************
74+
* Use case 3: Validation an API response *
75+
******************************************/
6976

7077
const userPattern = {
7178
name: P.string,
72-
// 6. optional properties
79+
// 1. optional properties
7380
age: P.optional(P.number),
7481
socialLinks: P.optional({
7582
twitter: P.string,
@@ -82,7 +89,7 @@ const postPattern = {
8289
content: P.string,
8390
likeCount: P.number,
8491
author: userPattern,
85-
// 7. arrays
92+
// 2. arrays
8693
comments: P.array({
8794
author: userPattern,
8895
content: P.string,
@@ -91,12 +98,17 @@ const postPattern = {
9198

9299
type Post = P.infer<typeof postPattern>;
93100

94-
const validate = (response: unknown): Post | null => {
95-
// 8. validating unknown input with isMatching
101+
// elsewhere ine the code:
102+
// `fetch(...).then(validateResponse)`
103+
104+
const validateResponse = (response: unknown): Post | null => {
105+
// 3. validating unknown input with isMatching
96106
if (isMatching(postPattern, response)) {
97-
// response: Post
98-
const cardTitle = `${response.title} by @${response.author.name}`;
99-
const commenters = response.comments.map((c) => c.author.name).join(', ');
107+
// response is inferred as `Post`.
108+
109+
// Uncomment these lines if you want to try using `response`:
110+
// const cardTitle = `${response.title} by @${response.author.name}`;
111+
// const commenters = response.comments.map((c) => c.author.name).join(', ');
100112
return response;
101113
}
102114

src/types/helpers.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ export type Values<a extends object> = UnionToTuple<ValueOf<a>>;
66
* ### LeastUpperBound
77
* An interesting one. A type taking two imbricated sets and returning the
88
* smallest one.
9-
* We need that because sometimes the pattern's infered type holds more
9+
* We need that because sometimes the pattern's inferred type holds more
1010
* information than the value on which we are matching (if the value is any
1111
* or unknown for instance).
1212
*/

tests/type-error.test.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ describe('type errors', () => {
3030
.exhaustive();
3131
});
3232

33-
it("If the pattern's wrong, the infered selection must be the input type", () => {
33+
it("If the pattern's wrong, the inferred selection must be the input type", () => {
3434
match<Country>('Germany')
3535
.with('Germany', 'Spain', () => 'Europe')
3636
// @ts-expect-error: 'US' instead of 'USA'

tests/when.test.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -292,7 +292,7 @@ describe('when', () => {
292292
// handler to be executed.
293293
.with(
294294
{ kind: 'some' },
295-
// `someNumber` is infered to be a { kind: "some"; value: number }
295+
// `someNumber` is inferred to be a { kind: "some"; value: number }
296296
// based on the pattern provided as first argument.
297297
(someNumber) =>
298298
someNumber.value % 5 === 0 && someNumber.value % 3 === 0,

0 commit comments

Comments
 (0)