如果你不尊敬你的父母,那你的孩子也将不会尊敬你。——佚名

题目:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
/*

Intro:

We have asynchronous functions now, advanced technology.
This makes us a tech startup officially now.
But one of the consultants spoiled our dreams about
inevitable future IT leadership.
He said that callback-based asynchronicity is not
popular anymore and everyone should use Promises.
He promised that if we switch to Promises, this would
bring promising results.

Exercise:

We don't want to reimplement all the data-requesting
functions. Let's decorate the old callback-based
functions with the new Promise-compatible result.
The final function should return a Promise which
would resolve with the final data directly
(i.e. users or admins) or would reject with an error
(or type Error).

The function should be named promisify.

Higher difficulty bonus exercise:

Create a function promisifyAll which accepts an object
with functions and returns a new object where each of
the function is promisified.

Rewrite api creation accordingly:

const api = promisifyAll(oldApi);

*/

interface User {
type: 'user';
name: string;
age: number;
occupation: string;
}

interface Admin {
type: 'admin';
name: string;
age: number;
role: string;
}

type Person = User | Admin;

const admins: Admin[] = [
{ type: 'admin', name: 'Jane Doe', age: 32, role: 'Administrator' },
{ type: 'admin', name: 'Bruce Willis', age: 64, role: 'World saver' }
];

const users: User[] = [
{ type: 'user', name: 'Max Mustermann', age: 25, occupation: 'Chimney sweep' },
{ type: 'user', name: 'Kate Müller', age: 23, occupation: 'Astronaut' }
];

export type ApiResponse<T> = (
{
status: 'success';
data: T;
} |
{
status: 'error';
error: string;
}
);

export function promisify(arg: unknown): unknown {
return null;
}

const oldApi = {
requestAdmins(callback: (response: ApiResponse<Admin[]>) => void) {
callback({
status: 'success',
data: admins
});
},
requestUsers(callback: (response: ApiResponse<User[]>) => void) {
callback({
status: 'success',
data: users
});
},
requestCurrentServerTime(callback: (response: ApiResponse<number>) => void) {
callback({
status: 'success',
data: Date.now()
});
},
requestCoffeeMachineQueueLength(callback: (response: ApiResponse<number>) => void) {
callback({
status: 'error',
error: 'Numeric value has exceeded Number.MAX_SAFE_INTEGER.'
});
}
};

export const api = {
requestAdmins: promisify(oldApi.requestAdmins),
requestUsers: promisify(oldApi.requestUsers),
requestCurrentServerTime: promisify(oldApi.requestCurrentServerTime),
requestCoffeeMachineQueueLength: promisify(oldApi.requestCoffeeMachineQueueLength)
};

function logPerson(person: Person) {
console.log(
` - ${person.name}, ${person.age}, ${person.type === 'admin' ? person.role : person.occupation}`
);
}

async function startTheApp() {
console.log('Admins:');
(await api.requestAdmins()).forEach(logPerson);
console.log();

console.log('Users:');
(await api.requestUsers()).forEach(logPerson);
console.log();

console.log('Server time:');
console.log(` ${new Date(await api.requestCurrentServerTime()).toLocaleString()}`);
console.log();

console.log('Coffee machine queue length:');
console.log(` ${await api.requestCoffeeMachineQueueLength()}`);
}

startTheApp().then(
() => {
console.log('Success!');
},
(e: Error) => {
console.log(`Error: "${e.message}", but it's fine, sometimes errors are inevitable.`);
}
);

// In case you are stuck:
// https://www.typescriptlang.org/docs/handbook/2/generics.html

报错:

1
2
3
4
5
6
7
8
index.ts(121,12): error TS2571: Object is of type 'unknown'.
index.ts(125,12): error TS2571: Object is of type 'unknown'.
index.ts(129,38): error TS2571: Object is of type 'unknown'.
index.ts(133,29): error TS2571: Object is of type 'unknown'.
test.ts(30,5): error TS2344: Type 'false' does not satisfy the constraint 'true'.
test.ts(44,5): error TS2344: Type 'false' does not satisfy the constraint 'true'.
test.ts(58,5): error TS2344: Type 'false' does not satisfy the constraint 'true'.
test.ts(65,5): error TS2344: Type 'false' does not satisfy the constraint 'true'.

答案:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
/*

Intro:

We have asynchronous functions now, advanced technology.
This makes us a tech startup officially now.
But one of the consultants spoiled our dreams about
inevitable future IT leadership.
He said that callback-based asynchronicity is not
popular anymore and everyone should use Promises.
He promised that if we switch to Promises, this would
bring promising results.

Exercise:

We don't want to reimplement all the data-requesting
functions. Let's decorate the old callback-based
functions with the new Promise-compatible result.
The final function should return a Promise which
would resolve with the final data directly
(i.e. users or admins) or would reject with an error
(or type Error).

The function should be named promisify.

Higher difficulty bonus exercise:

Create a function promisifyAll which accepts an object
with functions and returns a new object where each of
the function is promisified.

Rewrite api creation accordingly:

const api = promisifyAll(oldApi);

*/

interface User {
type: 'user';
name: string;
age: number;
occupation: string;
}

interface Admin {
type: 'admin';
name: string;
age: number;
role: string;
}

type Person = User | Admin;

const admins: Admin[] = [
{ type: 'admin', name: 'Jane Doe', age: 32, role: 'Administrator' },
{ type: 'admin', name: 'Bruce Willis', age: 64, role: 'World saver' }
];

const users: User[] = [
{ type: 'user', name: 'Max Mustermann', age: 25, occupation: 'Chimney sweep' },
{ type: 'user', name: 'Kate Müller', age: 23, occupation: 'Astronaut' }
];

export type ApiResponse<T> = (
{
status: 'success';
data: T;
} |
{
status: 'error';
error: string;
}
);

export function promisify<T>(arg: (callback: (response: ApiResponse<T>) => void) => void): () => Promise<T> {
return () => new Promise<T>((resolve, reject) => arg(response => response.status === 'success'? resolve(response.data) : reject(response.error)));
}

const oldApi = {
requestAdmins(callback: (response: ApiResponse<Admin[]>) => void) {
callback({
status: 'success',
data: admins
});
},
requestUsers(callback: (response: ApiResponse<User[]>) => void) {
callback({
status: 'success',
data: users
});
},
requestCurrentServerTime(callback: (response: ApiResponse<number>) => void) {
callback({
status: 'success',
data: Date.now()
});
},
requestCoffeeMachineQueueLength(callback: (response: ApiResponse<number>) => void) {
callback({
status: 'error',
error: 'Numeric value has exceeded Number.MAX_SAFE_INTEGER.'
});
}
};

export const api = {
requestAdmins: promisify(oldApi.requestAdmins),
requestUsers: promisify(oldApi.requestUsers),
requestCurrentServerTime: promisify(oldApi.requestCurrentServerTime),
requestCoffeeMachineQueueLength: promisify(oldApi.requestCoffeeMachineQueueLength)
};

function logPerson(person: Person) {
console.log(
` - ${person.name}, ${person.age}, ${person.type === 'admin' ? person.role : person.occupation}`
);
}

async function startTheApp() {
console.log('Admins:');
(await api.requestAdmins()).forEach(logPerson);
console.log();

console.log('Users:');
(await api.requestUsers()).forEach(logPerson);
console.log();

console.log('Server time:');
console.log(` ${new Date(await api.requestCurrentServerTime()).toLocaleString()}`);
console.log();

console.log('Coffee machine queue length:');
console.log(` ${await api.requestCoffeeMachineQueueLength()}`);
}

startTheApp().then(
() => {
console.log('Success!');
},
(e: Error) => {
console.log(`Error: "${e.message}", but it's fine, sometimes errors are inevitable.`);
}
);

// In case you are stuck:
// https://www.typescriptlang.org/docs/handbook/2/generics.html

进阶答案:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
/*

Intro:

We have asynchronous functions now, advanced technology.
This makes us a tech startup officially now.
But one of the consultants spoiled our dreams about
inevitable future IT leadership.
He said that callback-based asynchronicity is not
popular anymore and everyone should use Promises.
He promised that if we switch to Promises, this would
bring promising results.

Exercise:

We don't want to reimplement all the data-requesting
functions. Let's decorate the old callback-based
functions with the new Promise-compatible result.
The final function should return a Promise which
would resolve with the final data directly
(i.e. users or admins) or would reject with an error
(or type Error).

The function should be named promisify.

Higher difficulty bonus exercise:

Create a function promisifyAll which accepts an object
with functions and returns a new object where each of
the function is promisified.

Rewrite api creation accordingly:

const api = promisifyAll(oldApi);

*/

interface User {
type: 'user';
name: string;
age: number;
occupation: string;
}

interface Admin {
type: 'admin';
name: string;
age: number;
role: string;
}

type Person = User | Admin;

const admins: Admin[] = [
{ type: 'admin', name: 'Jane Doe', age: 32, role: 'Administrator' },
{ type: 'admin', name: 'Bruce Willis', age: 64, role: 'World saver' }
];

const users: User[] = [
{ type: 'user', name: 'Max Mustermann', age: 25, occupation: 'Chimney sweep' },
{ type: 'user', name: 'Kate Müller', age: 23, occupation: 'Astronaut' }
];

export type ApiResponse<T> = (
{
status: 'success';
data: T;
} |
{
status: 'error';
error: string;
}
);

export function promisify<T>(arg: ApiFunc<T>): PromisifiedFunc<T> {
return () => new Promise<T>((resolve, reject) => arg(response => response.status === 'success'? resolve(response.data) : reject(response.error)));
}

// 定义API请求范型方法
export type ApiFunc<T> = (callback: (response: ApiResponse<T>) => void) => void;
// 表明该类里全是这种范型方法
export type ApiObj<T> = {[K in keyof T]: ApiFunc<T[K]>}

// 定义Promise范型方法
export type PromisifiedFunc<T> = () => Promise<T>
// 表明该类里全是这种范型方法
export type PromisifiedObj<T> = {[K in keyof T]: PromisifiedFunc<T[K]>}

// 将全部成员换成调用promisify后的结果
export function promisifyAll<T extends {[key: string]: any}>(obj: ApiObj<T>): PromisifiedObj<T> {
const result: {[key:string]: PromisifiedFunc<any>} = {};
Object.keys(obj).forEach(key => result[key] = promisify(obj[key]))
return result as PromisifiedObj<T>
}

const oldApi = {
requestAdmins(callback: (response: ApiResponse<Admin[]>) => void) {
callback({
status: 'success',
data: admins
});
},
requestUsers(callback: (response: ApiResponse<User[]>) => void) {
callback({
status: 'success',
data: users
});
},
requestCurrentServerTime(callback: (response: ApiResponse<number>) => void) {
callback({
status: 'success',
data: Date.now()
});
},
requestCoffeeMachineQueueLength(callback: (response: ApiResponse<number>) => void) {
callback({
status: 'error',
error: 'Numeric value has exceeded Number.MAX_SAFE_INTEGER.'
});
}
};

export const api = promisifyAll(oldApi);

function logPerson(person: Person) {
console.log(
` - ${person.name}, ${person.age}, ${person.type === 'admin' ? person.role : person.occupation}`
);
}

async function startTheApp() {
console.log('Admins:');
(await api.requestAdmins()).forEach(logPerson);
console.log();

console.log('Users:');
(await api.requestUsers()).forEach(logPerson);
console.log();

console.log('Server time:');
console.log(` ${new Date(await api.requestCurrentServerTime()).toLocaleString()}`);
console.log();

console.log('Coffee machine queue length:');
console.log(` ${await api.requestCoffeeMachineQueueLength()}`);
}

startTheApp().then(
() => {
console.log('Success!');
},
(e: Error) => {
console.log(`Error: "${e.message}", but it's fine, sometimes errors are inevitable.`);
}
);

// In case you are stuck:
// https://www.typescriptlang.org/docs/handbook/2/generics.html

这里的映射类型文档:

https://www.typescriptlang.org/docs/handbook/2/mapped-types.html