Chapters - Table of contents →
Function overloads
1////////////////////////////////////////
2// 1. Type the following function by looking at its body and usage
3
4function formatDate(date, format = 'dd-mm-YYYY') {
5 let d = null
6 if (typeof date === 'string') {
7 d = parseISO(date)
8 } else if (typeof date === 'number') {
9 d = new Date(date)
10 } else if (date instanceof Date) {
11 d = date
12 } else {
13 return null
14 }
15 if (d.toString() === 'Invalid Date') {
16 return null
17 } else {
18 return fmtDate(d, format)
19 }
20}
21
22// SOLUTION:
23// function formatDate(
24// date: Date | string | number,
25// format = 'dd-mm-YYYY'
26// ): string | null {
27// let d = null
28// if (typeof date === 'string') {
29// d = parseISO(date)
30// } else if (typeof date === 'number') {
31// d = new Date(date)
32// } else if (date instanceof Date) {
33// d = date
34// } else {
35// return null
36// }
37// if (d.toString() === 'Invalid Date') {
38// return null
39// } else {
40// return fmtDate(d, format)
41// }
42// }
43
44////////////////////////////////////////
45// 2. Type the following function by looking at:
46// - its body
47// - the types defined for the different types of `Post`
48
49interface Post {
50 id: string
51 createdAt: Date
52 updatedAt: Date
53 description: string
54 title: string
55 type: 'engineering' | 'corporate'
56}
57
58interface PostEngineering extends Post {
59 type: 'engineering'
60 technologies: ('ruby' | 'javascript')[]
61 githubLink: string
62}
63
64interface PostCorporate extends Post {
65 type: 'corporate'
66 tags: string[]
67 linkedArticles: string[]
68}
69
70// `type` can either be 'engineering' or 'corporate'.
71function filterPostByType(posts, type = 'corporate') {
72 return posts.filter((p) => p.type === type)
73}
74
75// SOLUTION:
76// function filterPostByType(posts: Post[], type: 'corporate'): PostCorporate[];
77// function filterPostByType(posts: Post[], type: 'engineering'): PostEngineering[];
78// function filterPostByType(posts: Post[], type: 'engineering' | 'corporate'): any[] {
79// return posts.filter(p => p.type === type)
80// }
81
82////////////////////////////////////////
83// 3. Rewrite the correct type definition of `useRef()` with the following types and
84// usage examples
85
86// SOLUTION:
87// interface RefObject {
88// readonly current: any
89// }
90
91// interface MutableRefObject {
92// current: any
93// }
94
95// interface UseRef {
96// (initialValue: any): RefObject;
97// (): MutableRefObject;
98// }
99
100// const useRef: UseRef = (ref?: any): MutableRefObject | RefObject => {
101// return {
102// current: null
103// }
104// }
105
Generics
1////////////////////////////////////////
2// 1. Type the following custom hook by looking at its body and usage:
3
4// keeps a localstorage value in sync with React state
5const useStorage = (name, initialValue = {}) => {
6 const [value, setValue] = useState(initialValue)
7
8 useEffect(() => {
9 const t = setTimeout(() => {
10 if (value !== localStorage.getItem(name)) {
11 setValue(JSON.parse(localStorage.getItem(name) || '{}'))
12 }
13 }, 500)
14 return () => clearTimeout(t)
15 })
16
17 const update = useCallback(
18 (val) => {
19 localStorage.setItem(name, JSON.stringify(val || '{}'))
20 setValue(val)
21 },
22 [name, setValue]
23 )
24
25 return {
26 value,
27 update,
28 }
29}
30
31// SOLUTION:
32// function useStorage <T extends object>(name: string, initialValue: T): {
33// value: T,
34// update(val: T): void
35// } {
36// const [value, setValue] = useState<T>(initialValue)
37
38// useEffect(() => {
39// const t = setTimeout(() => {
40// const val = JSON.parse(localStorage.getItem(name) || '{}')
41// if (value !== val) {
42// setValue(val)
43// }
44// }, 500)
45// return () => clearTimeout(t)
46// })
47
48// const update = useCallback(
49// (val) => {
50// localStorage.setItem(name, JSON.stringify(val || '{}'))
51// setValue(val)
52// },
53// [name, setValue]
54// )
55
56// return {
57// value,
58// update,
59// }
60// }
61
62
63
64////////////////////////////////////////
65// 2. Type the following function looking at its body and usage:
66
67function reduce(arr, iterator, initialValue = undefined) {
68 let acc = initialValue
69 for (let index = 0; index < arr.length; index++) {
70 acc = iterator(acc, arr[index])
71 }
72 return acc
73}
74
75// Usage:
76
77reduce([1, 2, 3, 4], (sum, value) => sum + value, 0)
78
79reduce(
80 [1, 2, 3, 4],
81 (acc, value) => {
82 acc.total += value
83 return acc
84 },
85 { total: 0 }
86)
87
88reduce([1, 2, 3, 4], (acc, value) => {
89 if (!acc) {
90 acc = { result: value }
91 } else {
92 acc.result = value - acc.result
93 }
94 return acc
95})
96
97// SOLUTION:
98// function reduce<T, A = undefined>(arr: T[], iterator: (acc: A, val: T) => A, initialValue?: A) {
99// let acc = initialValue
100// for (let index = 0; index < arr.length; index++) {
101// acc = iterator(acc, arr[index])
102// }
103// return acc
104// }
105
106// // Usage:
107
108// reduce([1, 2, 3, 4], (sum, value) => sum + value, 0)
109
110// reduce([1, 2, 3, 4], (acc, value) => {
111// acc.total += value
112// return acc
113// }, { total: 0 })
114
115// reduce<number, { result: number }>([1, 2, 3, 4], (acc, value) => {
116// if (!acc) {
117// acc = { result: value }
118// } else {
119// acc.result = value - acc.result
120// }
121// return acc
122// })
123
124////////////////////////////////////////
125// 3. Provide the proper typings for the following function:
126
127function compose(fn1, fn2) {
128 return (initialArg) => fn1(fn2(initialArg))
129}
130
131const a = compose(
132 (num) => num.toString(),
133 (initialNumber: number) => initialNumber + 2
134)
135// a is expected to be of type `(a: number) => string`
136
137// SOLUTION:
138// function compose<R3, R2, R1>(fn1: (arg: R3) => R2, fn2: (arg: R1) => R3): (initialArg: R1) => R2 {
139// return (initialArg: R1) => fn1(fn2(initialArg))
140// }
141
142// const a = compose((a) => a.toString(), (a: number) => a + 2)
143
144