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