Skip to content

Commit

Permalink
Add exercise steps
Browse files Browse the repository at this point in the history
  • Loading branch information
simonplend committed Aug 23, 2024
1 parent 022b9e3 commit 0a63224
Show file tree
Hide file tree
Showing 3 changed files with 528 additions and 5 deletions.
76 changes: 74 additions & 2 deletions 01-composing-types-part-1/exercise/README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,77 @@
# Exercise: Blast from the Past
# Exercise: Blast from the Past 🏛️

> **Section 1: Composing Types: Part I**
In this exercise we're going to build up the types for objects which
contain data on ancient civilizations. We'll use generic and conditional
types to help us.
types to help us along the way.

## Create the `Civilization` type

```typescript
interface Civilization {
name: string;
location: string;
}
```

## Turn the `Civilization` type into a generic type

- Add a type parameter to the `Civilization` type:

```diff
-interface Civilization {
+interface Civilization<NotablePeopleType> {
```

- Add a property which uses the type variable:

```typescript
notablePeople: NotablePeopleType[];
```

## Create types for all the notable people

```typescript
interface Person {
name: string;
occupation: string;
}

type Architect = Person & { occupation: 'Architect' };
type Pharaoh = Person & { occupation: 'Pharaoh' };
type Poet = Person & { occupation: 'Poet' };
type Philosopher = Person & { occupation: 'Philosopher' };
type General = Person & { occupation: 'General' };
```

## Add type annotations to each civilization object

Example:

```typescript
const egyptianCivilization: Civilization<Architect | Pharaoh> = {
```
## Notice that we can pass anything in for the `NotablePeopleType` argument
Example:
```typescript
Civilization<NotablePeople<Architect | true>>
```
## Create a generic type with a conditional type
```typescript
type NotablePeople<PersonType> = PersonType extends Person ? PersonType : never;
```
## Update the type annotations for all civilization objects
Example:
```diff
-const egyptianCivilization: Civilization<Architect | Pharaoh> = {
+const egyptianCivilization: Civilization<NotablePeople<Architect | Pharaoh>> = {
```
101 changes: 99 additions & 2 deletions 01-composing-types-part-2/exercise/README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,101 @@
# Exercise: Tree-mendous Mapped Types
# Exercise: Tree-mendous Mapped Types 🌳

> **Section 1: Composing Types: Part II**
In this exercise we're going to use one object type to create
another object type. It's tree-mendously powerful!
another object type. We'll see just how tree-mendously powerful
mapped types really are!

## Part 1

### Create a `Tree` type

```typescript
type Tree = {
name: string;
height: number;
age: number;
};
```

### Use the type

```diff
-const oakData = {
+const oakData: Tree = {
```

### Create `TreeDetails` mapped type to describe `oak` object

```typescript
type TreeDetails<Type> = {
[Key in keyof Type as `get${Capitalize<string & Key>}`]: () => Type[Key];
};

type OakTree = TreeDetails<Tree>;
```

### Components to discuss

- `keyof Type`
- `Key in keyof Type` — same as `Key in "name" | "height" | "age"`
- `as` - type assertion
- `Capitalize<string & Key>` — using the `Capitalize` utility type
- `string & Key` - ensures that the `Key` extends the `string` type, as keys can be Symbols in JavaScript
- `() => Type[Key]` - function signature
- `Type[Key]` - indexed access type to look up type of property on `Type`

### Alternative for extracting keys with type string

```typescript
Extract<keyof Type, string>
```

### Use the `OakTree` type

```diff
-let oak = {
+let oak: OakTree = {
getName: () => oakData.name,
getHeight: () => oakData.height,
getAge: () => oakData.age,
};
```

## Optional: Part 2: Create a `PartialTree` mapped type

```typescript
type PartialTree<Type> = {
[Key in keyof Type]+?: Type[Key];
};

// Using the `+?` optionality modifier to indicate the field is optional.

type PartialOak = PartialTree<Tree>;

let partialOak: PartialOak = {
name: "Oak",
};

console.log({ partialOak });
```

## Optional: Part 3: Create a generic type with a template literal type

```typescript
type TreeWithAge<Type extends number> = `Tree with age ${Type}`;

type OldTree = TreeWithAge<number>;

let oldOak: OldTree = `Tree with age ${oak.getAge()}`;

console.log(oldOak);
```

### Alternative to hardcoding `number` as the argument to `TreeWithAge`

Use an indexed access type:

```typescript
ReturnType<OakTree["getAge"]>
```
Loading

0 comments on commit 0a63224

Please sign in to comment.