Exercises answers - Part 2
Chapter 3: TypeScript for real-world applications
Chapters - Table of contents

Type helpers

1//////////////////////////////
2// 1. Given the following `User` type, build the following `UserForm` type using `Omit<>`:
3
4interface User {
5  id: string
6  firstName: string
7  lastName: string
8  address: string
9  zipcode: string
10}
11
12// `UserForm` should be:
13// {
14//   firstName?: string
15//   lastName?: string
16//   address?: string
17//   zipcode?: string
18// }
19
20// SOLUTION:
21// type UserForm = Partial<Omit<User, 'id'>>
22
23//////////////////////////////
24// 2. Build the `UserForm` above using `Pick<>` instead of `Omit<>`
25
26// SOLUTION:
27// type UserForm = Partial<Pick<User, 'firstName' | 'lastName' | 'address' | 'zipcode'>>
28
29
30//////////////////////////////
31// 3. Let's say you want to proxy an existing logging function `logger()`
32//    Leverage TypeScript type helpers and type lookup to avoid code duplication
33
34const logger = (level: 'warning' | 'log' | 'danger', message: string): void => {
35  // log ...
36}
37
38const customLogger = (level, message, context) =>
39  logger(level, JSON.stringify({ message, context }))
40
41// SOLUTION
42// interface CustomLogger {
43//   (
44//     level: Parameters<typeof logger>['0'],
45//     message: Parameters<typeof logger>['1'],
46//     context: object,
47//   ): void
48// }
49
50// const customLogger: CustomLogger = (level, message, context) =>
51//   logger(level, JSON.stringify({ message, context }))
52

Mapped types

1/////////////////////////////////
2// 1. Let the following `Config` type have a `meta` property accept additional properties
3//  with key as string and value as string or number
4
5interface Config {
6  srcPath: string
7  distPath: string
8}
9
10const config: Config = {
11  srcPath: 'src/',
12  distPath: 'dist/',
13  meta: {
14    test: 1,
15    test2: 'hi',
16  },
17}
18
19// SOLUTION:
20// interface Config {
21//   srcPath: string
22//   distPath: string
23//   meta: {
24//     [k: string]: string | number
25//   }
26// }
27
28/////////////////////////////////
29// 2. Write a `ToNumber<T>` type that transform all `T` properties to `number`:
30
31// SOLUTION:
32// type ToNumber<T> = {
33//   [k in keyof T]: number
34// }
35
36/////////////////////////////////
37// 3. Write a `RequiredProperties<T, K>` type that would behave as follow:
38
39interface Company {
40  name?: string
41  taxId: string
42  employeeCount: number
43  address?: string
44}
45
46type RequiredCompany = RequiredProperties<Company, 'taxId' | 'name'>
47
48// `RequiredCompany` is
49// {
50//   name?: string
51//   taxId: string
52//   employeeCount: number
53//   address?: string
54// }
55
56
57// SOLUTION:
58// type RequiredProperties<T extends object, P_LIST extends keyof T> = T & Required<Pick<T, P_LIST>>
59
60/////////////////////////////////
61// 4. GraphQL objects often have unnecessary `null | undefined` types
62//    Make a type `RequiredGraphQLProperties<T, K>` that would remove
63//    `null | undefined` from a list `K` of given properties as follows:
64
65type Maybe<T> = T | null | undefined
66interface User {
67  id: Maybe<string>
68  firstName: Maybe<string>
69  lastName: Maybe<string>
70  address: Maybe<string>
71}
72
73type CleanUser = RequiredGraphQLProperties<User, keyof User>
74// `CleanUser` is
75// {
76//   id: string
77//   firstName: string
78//   lastName: string
79//   address: string
80// }
81
82// SOLUTION:
83// type RequiredGraphQLProperties<T extends object, P_LIST extends keyof T> = T & {
84//   [P in keyof Pick<T, P_LIST>]-?: NonNullable<T[P]>
85// }
86
We use cookies to collect statistics through Google Analytics.
Do not track
 
Allow cookies