Skip to content

Commit 27ed311

Browse files
committed
Added challenges and explainers
1 parent 722eb75 commit 27ed311

15 files changed

+523
-0
lines changed
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
// Explain excalidraw's sum function
2+
3+
export {};
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { Equal, Expect } from "../helpers/type-utils";
2+
3+
export const inferItemLiteral = <T extends string | number>(t: T) => {
4+
return {
5+
output: t,
6+
};
7+
};
8+
9+
const result1 = inferItemLiteral("a");
10+
const result2 = inferItemLiteral(123);
11+
12+
type tests = [
13+
Expect<Equal<typeof result1, { output: "a" }>>,
14+
Expect<Equal<typeof result2, { output: 123 }>>,
15+
];
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import { CSSProperties } from "react";
2+
3+
/**
4+
* In this implementation, we need to specify the theme
5+
* inside useStyled wherever we use it. This is not ideal.
6+
*
7+
* See if you can refactor useStyled into a function called
8+
* makeUseStyled which returns a useStyled function, typed
9+
* with the theme.
10+
*
11+
* Desired API:
12+
*
13+
* const useStyled = makeUseStyled<MyTheme>();
14+
*/
15+
const useStyled = <TTheme = {}>(func: (theme: TTheme) => CSSProperties) => {
16+
// Imagine that this function hooks into a global theme
17+
// and returns the CSSProperties
18+
return {} as CSSProperties;
19+
};
20+
21+
interface MyTheme {
22+
color: {
23+
primary: string;
24+
};
25+
fontSize: {
26+
small: string;
27+
};
28+
}
29+
30+
const buttonStyle = useStyled<MyTheme>((theme) => ({
31+
color: theme.color.primary,
32+
fontSize: theme.fontSize.small,
33+
}));
34+
35+
const divStyle = useStyled<MyTheme>((theme) => ({
36+
backgroundColor: theme.color.primary,
37+
}));
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import { CSSProperties } from "react";
2+
3+
const makeUseStyled = <TTheme = {}>() => {
4+
const useStyled = (func: (theme: TTheme) => CSSProperties) => {
5+
// Imagine that this function hooks into a global theme
6+
// and returns the CSSProperties
7+
return {} as CSSProperties;
8+
};
9+
10+
return useStyled;
11+
};
12+
13+
const useStyled = makeUseStyled<MyTheme>();
14+
15+
interface MyTheme {
16+
color: {
17+
primary: string;
18+
};
19+
fontSize: {
20+
small: string;
21+
};
22+
}
23+
24+
const buttonStyle = useStyled((theme) => ({
25+
color: theme.color.primary,
26+
fontSize: theme.fontSize.small,
27+
}));
28+
29+
const divStyle = useStyled((theme) => ({
30+
backgroundColor: theme.color.primary,
31+
}));
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import { Equal, Expect } from "../helpers/type-utils";
2+
3+
export const makeSelectors = <
4+
TSource,
5+
TSelectors extends Record<string, (source: TSource) => any> = {},
6+
>(
7+
selectors: TSelectors,
8+
) => {
9+
return selectors;
10+
};
11+
12+
interface Source {
13+
firstName: string;
14+
middleName: string;
15+
lastName: string;
16+
}
17+
18+
/**
19+
* We've got a problem here. We want to be able to infer the type
20+
* of the selectors object from what we passed in to makeSelectors.
21+
*
22+
* But we can't. As soon as we pass ONE type argument, inference
23+
* doesn't work on the other type arguments. We want to refactor this
24+
* so that we can infer the type of the selectors object.
25+
*
26+
* Desired API:
27+
*
28+
* makeSelectors<Source>()({ ...selectorsGoHere })
29+
*/
30+
const selectors = makeSelectors<Source>({
31+
getFullName: (source) =>
32+
`${source.firstName} ${source.middleName} ${source.lastName}`,
33+
getFirstAndLastName: (source) => `${source.firstName} ${source.lastName}`,
34+
getFirstNameLength: (source) => source.firstName.length,
35+
});
36+
37+
type tests = [
38+
Expect<Equal<typeof selectors["getFullName"], (source: Source) => string>>,
39+
Expect<
40+
Equal<typeof selectors["getFirstAndLastName"], (source: Source) => string>
41+
>,
42+
Expect<
43+
Equal<typeof selectors["getFirstNameLength"], (source: Source) => number>
44+
>,
45+
];
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import { Equal, Expect } from "../helpers/type-utils";
2+
3+
export const makeSelectors =
4+
<TSource = "Type argument expected in makeSelectors">() =>
5+
<TSelectors extends Record<string, (source: TSource) => any>>(
6+
selectors: TSelectors
7+
) => {
8+
return selectors;
9+
};
10+
11+
interface Source {
12+
firstName: string;
13+
middleName: string;
14+
lastName: string;
15+
}
16+
17+
const selectors = makeSelectors<Source>()({
18+
getFullName: (source) =>
19+
`${source.firstName} ${source.middleName} ${source.lastName}`,
20+
getFirstAndLastName: (source) => `${source.firstName} ${source.lastName}`,
21+
getFirstNameLength: (source) => source.firstName.length,
22+
});
23+
24+
type tests = [
25+
Expect<Equal<typeof selectors["getFullName"], (source: Source) => string>>,
26+
Expect<
27+
Equal<typeof selectors["getFirstAndLastName"], (source: Source) => string>
28+
>,
29+
Expect<
30+
Equal<typeof selectors["getFirstNameLength"], (source: Source) => number>
31+
>
32+
];

src/06-challenges/31-pick.problem.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import { expect, it } from "vitest";
2+
import { Equal, Expect } from "../helpers/type-utils";
3+
4+
const pick = (obj: unknown, pick: unknown) => {};
5+
6+
it("Should pick the keys from the object", () => {
7+
const result = pick(
8+
{
9+
a: 1,
10+
b: 2,
11+
c: 3,
12+
},
13+
["a", "b"]
14+
);
15+
16+
expect(result).toEqual({ a: 1, b: 2 });
17+
18+
type test = Expect<Equal<typeof result, { a: number; b: number }>>;
19+
});
20+
21+
it("Should not allow you to pass keys which do not exist in the object", () => {
22+
pick(
23+
{
24+
a: 1,
25+
b: 2,
26+
c: 3,
27+
},
28+
[
29+
"a",
30+
"b",
31+
// @ts-expect-error
32+
"d",
33+
]
34+
);
35+
});

src/06-challenges/31-pick.solution.ts

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import { expect, it } from "vitest";
2+
import { Equal, Expect } from "../helpers/type-utils";
3+
4+
const pick = <TObj, TPicked extends keyof TObj>(obj: TObj, pick: TPicked[]) => {
5+
return pick.reduce((acc, key) => {
6+
acc[key] = obj[key];
7+
return acc;
8+
}, {} as Pick<TObj, TPicked>);
9+
};
10+
11+
it("Should pick the keys from the object", () => {
12+
const result = pick(
13+
{
14+
a: 1,
15+
b: 2,
16+
c: 3,
17+
},
18+
["a", "b"]
19+
);
20+
21+
expect(result).toEqual({ a: 1, b: 2 });
22+
23+
type test = Expect<Equal<typeof result, { a: number; b: number }>>;
24+
});
25+
26+
it("Should not allow you to pass keys which do not exist in the object", () => {
27+
pick(
28+
{
29+
a: 1,
30+
b: 2,
31+
c: 3,
32+
},
33+
[
34+
"a",
35+
"b",
36+
// @ts-expect-error
37+
"d",
38+
]
39+
);
40+
});
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { expect, it } from "vitest";
2+
import { Equal, Expect } from "../helpers/type-utils";
3+
4+
const fetchData = async <TResult>(url: string): Promise<TResult> => {
5+
const data = await fetch(url).then((response) => response.json());
6+
return data;
7+
};
8+
9+
it("Should fetch data from an API", async () => {
10+
const data = await fetchData<{ name: string }>(
11+
"https://swapi.dev/api/people/1",
12+
);
13+
expect(data.name).toEqual("Luke Skywalker");
14+
15+
type tests = [Expect<Equal<typeof data, { name: string }>>];
16+
});
17+
18+
it("Should force you to add a type annotation with a helpful error message", async () => {
19+
const data = await fetchData("https://swapi.dev/api/people/1");
20+
21+
type tests = [
22+
Expect<Equal<typeof data, "You must pass a type argument to fetchData">>,
23+
];
24+
});

0 commit comments

Comments
 (0)