1
1
/**
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`
15
11
*/
16
12
17
13
import { isMatching , match , P , __ } from '../src' ;
18
14
19
- /**
20
- * Use case 1: handling discriminated union types
21
- */
15
+ /**************************************************
16
+ * Use case 1: handling discriminated union types *
17
+ ************************************************** /
22
18
23
19
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 } [ ] } ;
27
23
28
- const exampleFunction1 = ( input : Response ) =>
24
+ const exampleFunction1 = ( input : Response ) : string =>
29
25
match ( input )
30
26
// 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 )
32
28
// 2. using select
33
29
. with (
34
- { type : 'image' , extension : 'gif' , src : P . select ( ) } ,
30
+ { type : 'image' , data : { extension : 'gif' , src : P . select ( ) } } ,
35
31
( value ) => value + '!!'
36
32
)
37
33
// 3. using P.union
38
34
. 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
+ } ,
40
39
( value ) => value + '!!'
41
40
)
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
44
48
. exhaustive ( ) ;
45
49
46
- /**
47
- * Use case 2: multi params conditional logic
48
- */
50
+ /**********************************************
51
+ * Use case 2: multi params conditional logic *
52
+ ********************************************** /
49
53
50
- type UserType = 'contributor' | 'spectator' ;
54
+ type UserType = 'editor' | 'viewer' ;
55
+ // Uncomment 'premium' to see exhaustive checking in action
51
56
type OrgPlan = 'basic' | 'pro' /* | 'premium' */ | 'enterprise' ;
52
57
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
55
60
match ( [ org , user ] as const )
56
61
. with ( [ 'basic' , __ ] , ( ) => `Please upgrade to unlock this feature!` )
62
+ // 2. `.with()` can take several patterns. It will match if one of them do.
57
63
. with (
58
- [ P . union ( 'pro' , 'enterprise' ) , 'spectator' ] ,
64
+ [ 'pro' , 'viewer' ] ,
65
+ [ 'enterprise' , 'viewer' ] ,
59
66
( ) => `Your account doesn't have permissions to use this feature`
60
67
)
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
64
71
. exhaustive ( ) ;
65
72
66
- /**
67
- * Use case 3: Validation an API response
68
- */
73
+ /******************************************
74
+ * Use case 3: Validation an API response *
75
+ ****************************************** /
69
76
70
77
const userPattern = {
71
78
name : P . string ,
72
- // 6 . optional properties
79
+ // 1 . optional properties
73
80
age : P . optional ( P . number ) ,
74
81
socialLinks : P . optional ( {
75
82
twitter : P . string ,
@@ -82,7 +89,7 @@ const postPattern = {
82
89
content : P . string ,
83
90
likeCount : P . number ,
84
91
author : userPattern ,
85
- // 7 . arrays
92
+ // 2 . arrays
86
93
comments : P . array ( {
87
94
author : userPattern ,
88
95
content : P . string ,
@@ -91,12 +98,17 @@ const postPattern = {
91
98
92
99
type Post = P . infer < typeof postPattern > ;
93
100
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
96
106
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(', ');
100
112
return response ;
101
113
}
102
114
0 commit comments