Skip to content

Commit ef28080

Browse files
committed
WIP
1 parent 7d5dd3d commit ef28080

10 files changed

+286
-8
lines changed

src/05-generics/33.5-type-helpers.problem.tsx

Whitespace-only changes.

src/05-generics/33.6-type-helpers-with-constraints.problem.tsx

Whitespace-only changes.
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import { ReactNode } from "react";
2+
import { Equal, Expect } from "../helpers/type-utils";
3+
4+
interface TableProps {
5+
rows: any[];
6+
renderRow: (row: any) => ReactNode;
7+
}
8+
9+
export class Table extends React.Component<TableProps> {
10+
render(): ReactNode {
11+
return (
12+
<table>
13+
<tbody>
14+
{this.props.rows.map((row) => (
15+
<tr>{this.props.renderRow(row)}</tr>
16+
))}
17+
</tbody>
18+
</table>
19+
);
20+
}
21+
}
22+
23+
const data = [
24+
{
25+
id: 1,
26+
name: "John",
27+
},
28+
];
29+
30+
export const Parent = () => {
31+
return (
32+
<div>
33+
<Table rows={data} renderRow={(row) => <td>{row.name}</td>} />
34+
<Table
35+
rows={data}
36+
renderRow={(row) => {
37+
type test = Expect<Equal<typeof row, { id: number; name: string }>>;
38+
return (
39+
<td>
40+
{
41+
// @ts-expect-error
42+
row.doesNotExist
43+
}
44+
</td>
45+
);
46+
}}
47+
></Table>
48+
</div>
49+
);
50+
};
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import { ReactNode } from "react";
2+
import { Equal, Expect } from "../helpers/type-utils";
3+
4+
interface TableProps<T> {
5+
rows: T[];
6+
renderRow: (row: T) => ReactNode;
7+
}
8+
9+
export class Table<T> extends React.Component<TableProps<T>> {
10+
render(): ReactNode {
11+
return (
12+
<table>
13+
<tbody>
14+
{this.props.rows.map((row) => (
15+
<tr>{this.props.renderRow(row)}</tr>
16+
))}
17+
</tbody>
18+
</table>
19+
);
20+
}
21+
}
22+
23+
const data = [
24+
{
25+
id: 1,
26+
name: "John",
27+
},
28+
];
29+
30+
export const Parent = () => {
31+
return (
32+
<div>
33+
<Table rows={data} renderRow={(row) => <td>{row.name}</td>} />
34+
<Table
35+
rows={data}
36+
renderRow={(row) => {
37+
type test = Expect<Equal<typeof row, { id: number; name: string }>>;
38+
return (
39+
<td>
40+
{
41+
// @ts-expect-error
42+
row.doesNotExist
43+
}
44+
</td>
45+
);
46+
}}
47+
></Table>
48+
</div>
49+
);
50+
};

src/05-generics/37-button-group.problem.tsx renamed to src/05-generics/37-generic-inference-through-multiple-helpers.problem.tsx

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
import { Equal, Expect } from "../helpers/type-utils";
22

3+
interface Button {
4+
value: string;
5+
label: string;
6+
}
7+
38
interface ButtonGroupProps {
4-
buttons: {
5-
value: string;
6-
label: string;
7-
}[];
9+
buttons: Button[];
810
onClick: (value: string) => void;
911
}
1012

src/05-generics/37-button-group.solution.tsx renamed to src/05-generics/37-generic-inference-through-multiple-helpers.solution.tsx

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
import { Equal, Expect } from "../helpers/type-utils";
22

3+
interface Button<TValue extends string> {
4+
value: TValue;
5+
label: string;
6+
}
7+
38
interface ButtonGroupProps<TValue extends string> {
4-
buttons: {
5-
value: TValue;
6-
label: string;
7-
}[];
9+
buttons: Button<TValue>[];
810
onClick: (value: TValue) => void;
911
}
1012

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import { createUser } from "fake-external-lib";
2+
import { useState } from "react";
3+
import { Equal, Expect } from "../helpers/type-utils";
4+
5+
type Mutation = (...args: any[]) => Promise<any>;
6+
7+
interface UseMutationReturn {
8+
mutate: Mutation;
9+
isLoading: boolean;
10+
}
11+
12+
interface UseMutationOptions {
13+
mutation: Mutation;
14+
}
15+
16+
export const useMutation = (opts: UseMutationOptions): UseMutationReturn => {
17+
const [isLoading, setIsLoading] = useState(false);
18+
19+
return {
20+
mutate: async (...args) => {
21+
setIsLoading(true);
22+
23+
try {
24+
const result = await opts.mutation(...args);
25+
return result;
26+
} catch (e) {
27+
throw e;
28+
} finally {
29+
setIsLoading(false);
30+
}
31+
},
32+
isLoading,
33+
};
34+
};
35+
36+
const mutation = useMutation({
37+
mutation: createUser,
38+
});
39+
40+
mutation.mutate({ name: "John Doe", email: "[email protected]" });
41+
42+
// @ts-expect-error email missing!
43+
mutation.mutate({ name: "John Doe" });
44+
45+
mutation.mutate(
46+
{
47+
name: "John Doe",
48+
49+
},
50+
{
51+
throwOnError: true,
52+
// @ts-expect-error extra prop
53+
extra: "oh dear",
54+
},
55+
);
56+
57+
type test = [
58+
Expect<Equal<typeof mutation.isLoading, boolean>>,
59+
Expect<
60+
Equal<
61+
typeof mutation.mutate,
62+
(
63+
user: { name: string; email: string },
64+
opts?: {
65+
throwOnError?: boolean;
66+
},
67+
) => Promise<{
68+
id: string;
69+
name: string;
70+
email: string;
71+
}>
72+
>
73+
>,
74+
];
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
import { createUser } from "fake-external-lib";
2+
import { useState } from "react";
3+
import { Equal, Expect } from "../helpers/type-utils";
4+
5+
type Mutation<TArgs extends any[], TResult> = (
6+
...args: TArgs
7+
) => Promise<TResult>;
8+
9+
interface UseMutationReturn<TArgs extends any[], TResult> {
10+
mutate: Mutation<TArgs, TResult>;
11+
isLoading: boolean;
12+
}
13+
14+
interface UseMutationOptions<TArgs extends any[], TResult> {
15+
mutation: Mutation<TArgs, TResult>;
16+
}
17+
18+
export const useMutation = <TArgs extends any[], TResult>(
19+
opts: UseMutationOptions<TArgs, TResult>,
20+
): UseMutationReturn<TArgs, TResult> => {
21+
const [isLoading, setIsLoading] = useState(false);
22+
23+
return {
24+
mutate: async (...args) => {
25+
setIsLoading(true);
26+
27+
try {
28+
const result = await opts.mutation(...args);
29+
return result;
30+
} catch (e) {
31+
throw e;
32+
} finally {
33+
setIsLoading(false);
34+
}
35+
},
36+
isLoading,
37+
};
38+
};
39+
40+
const mutation = useMutation({
41+
mutation: createUser,
42+
});
43+
44+
mutation.mutate({ name: "John Doe", email: "[email protected]" });
45+
46+
// @ts-expect-error email missing!
47+
mutation.mutate({ name: "John Doe" });
48+
49+
mutation.mutate(
50+
{
51+
name: "John Doe",
52+
53+
},
54+
{
55+
throwOnError: true,
56+
// @ts-expect-error extra prop
57+
extra: "oh dear",
58+
},
59+
);
60+
61+
type test = [
62+
Expect<Equal<typeof mutation.isLoading, boolean>>,
63+
Expect<
64+
Equal<
65+
typeof mutation.mutate,
66+
(
67+
user: { name: string; email: string },
68+
opts?: {
69+
throwOnError?: boolean;
70+
},
71+
) => Promise<{
72+
id: string;
73+
name: string;
74+
email: string;
75+
}>
76+
>
77+
>,
78+
];

src/05-generics/37.5-intro-to-const-generics.problem.tsx

Whitespace-only changes.

src/fake-external-lib/index.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,25 @@
1+
/**
2+
* A fake function to create a user
3+
*/
4+
export const createUser = (
5+
user: {
6+
name: string;
7+
email: string;
8+
},
9+
opts?: {
10+
throwOnError?: boolean;
11+
},
12+
): Promise<{
13+
id: string;
14+
name: string;
15+
email: string;
16+
}> => {
17+
return fetch("/user", {
18+
method: "POST",
19+
body: JSON.stringify(user),
20+
}).then((response) => response.json());
21+
};
22+
123
/**
224
* A fake auth token hook.
325
*/

0 commit comments

Comments
 (0)