Skip to content

Commit

Permalink
Section 2: Exercise: Library for a Library
Browse files Browse the repository at this point in the history
  • Loading branch information
simonplend committed Jun 4, 2024
1 parent a709221 commit 650c9b3
Show file tree
Hide file tree
Showing 9 changed files with 220 additions and 0 deletions.
6 changes: 6 additions & 0 deletions 02-improving-type-safety/exercise/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Exercise: Library for a Library

In this exercise, we'll be creating a library with the following functions:

- `describeItem(item: LibraryItem): void`
- `getMovieById(id: Movie['id']): Promise<string>`
68 changes: 68 additions & 0 deletions 02-improving-type-safety/exercise/completed/data.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import { type Brand } from './utils.js';

export interface Book {
type: 'book',
id: string;
title: string;
published: number;
author: string;
}

export interface Album {
type: 'album',
id: string;
title: string;
released: number;
artist: string;
}

export interface MotionPicture {
id: unknown;
title: string;
released: number;
director: string;
}

export interface Movie extends MotionPicture {
type: 'movie',
id: Brand<string, 'MovieId'>;
}

export interface Show extends MotionPicture {
type: 'show',
id: Brand<string, 'ShowId'>;
}

export type LibraryItem = Book | Album | Movie | Show;

export const book: Book = {
type: 'book',
id: '82f7c297-40ec-4e9f-90b0-a12f2b4980c1',
title: 'To Kill a Mockingbird',
published: 1960,
author: 'Harper Lee'
}

export const album: Album = {
type: 'album',
id: '1dc8dd44-245e-4a5f-b1b6-8c02fd2a9dc1',
title: 'The Dark Side of the Moon',
released: 1973,
artist: 'Pink Floyd'
};

export const movie: Movie = {
type: 'movie',
id: 'f928756a-d3d9-4db3-92ea-88100d03af47' as Movie['id'],
title: 'Point Break',
released: 1991,
director: 'Kathryn Bigelow'
};

export const show: Show = {
type: 'show',
id: 'c9a7b229-b48f-4407-a267-45ff7fe8ea60' as Show['id'],
title: 'Westworld',
released: 2016,
director: 'Jonathan Nolan'
};
80 changes: 80 additions & 0 deletions 02-improving-type-safety/exercise/completed/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import {
book,
album,
movie,
show,
type Book,
type Album,
type Movie,
type Show,
type LibraryItem,
} from "./data.js";

import { fetchApiData } from "./utils.js";

function isBook(item: LibraryItem): item is Book {
return item.type === "book";
}

function isAlbum(item: LibraryItem): item is Album {
return item.type === "album";
}

function isMovie(item: LibraryItem): item is Movie {
return item.type === "movie";
}

function isShow(item: LibraryItem): item is Show {
return item.type === "show";
}

function describeLibraryItem(item: LibraryItem) {
if (isBook(item)) {
console.log(
`- ${item.title} is a book written by ${item.author}, and published in ${item.published}.`
);
} else if (isAlbum(item)) {
console.log(
`- ${item.title} is an album by the artist ${item.artist}, released in ${item.released}`
);
} else if (isMovie(item)) {
console.log(
`- ${item.title} is a movie directed by ${item.director}, released in ${item.released}`
);
} else if (isShow(item)) {
console.log(
`- ${item.title} is a show directed by ${item.director}, released in ${item.released}`
);
} else {
console.error("Error: Unrecognised library item!");
}
}

describeLibraryItem(book);
describeLibraryItem(album);
describeLibraryItem(movie);
describeLibraryItem(show);

function assertIsMovie(item: LibraryItem): asserts item is Movie {
if (item.type !== 'movie') {
throw new Error(`This item is not a Movie: ${item.title}`);
}
}

async function getMovieById(id: Movie['id']) {
const apiData = await fetchApiData();

const movie = apiData.find((item: LibraryItem) => item.id === id);

if (!movie) {
throw new Error('Movie not found');
}

assertIsMovie(movie);

return movie;
}

const movieDetails = await getMovieById(movie.id);

console.log(movieDetails);
3 changes: 3 additions & 0 deletions 02-improving-type-safety/exercise/completed/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"extends": "@total-typescript/tsconfig/tsc/no-dom/app"
}
13 changes: 13 additions & 0 deletions 02-improving-type-safety/exercise/completed/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { movie, show, book, album } from "./data.js";

export async function fetchApiData() {
const data = [movie, show, book, album];
const apiResponse = structuredClone(data);
return apiResponse;
}

declare const __brand: unique symbol;

export type Brand<BaseType, BrandedName extends string> = BaseType & {
[__brand]: BrandedName;
};
31 changes: 31 additions & 0 deletions 02-improving-type-safety/exercise/start/data.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
export const book = {
type: 'book',
id: '82f7c297-40ec-4e9f-90b0-a12f2b4980c1',
title: 'To Kill a Mockingbird',
published: 1960,
author: 'Harper Lee'
}

export const album = {
type: 'album',
id: '1dc8dd44-245e-4a5f-b1b6-8c02fd2a9dc1',
title: 'The Dark Side of the Moon',
released: 1973,
artist: 'Pink Floyd'
};

export const movie = {
type: 'movie',
id: 'f928756a-d3d9-4db3-92ea-88100d03af47',
title: 'Point Break',
released: 1991,
director: 'Kathryn Bigelow'
};

export const show = {
type: 'show',
id: 'c9a7b229-b48f-4407-a267-45ff7fe8ea60',
title: 'Westworld',
released: 2016,
director: 'Jonathan Nolan'
};
3 changes: 3 additions & 0 deletions 02-improving-type-safety/exercise/start/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { book, album, movie, show } from "./data.js";

import { fetchApiData } from "./utils.js";
3 changes: 3 additions & 0 deletions 02-improving-type-safety/exercise/start/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"extends": "@total-typescript/tsconfig/tsc/no-dom/app"
}
13 changes: 13 additions & 0 deletions 02-improving-type-safety/exercise/start/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { movie, show, book, album } from "./data.js";

export async function fetchApiData() {
const data = [movie, show, book, album];
const apiResponse = structuredClone(data);
return apiResponse;
}

declare const __brand: unique symbol;

export type Brand<BaseType, BrandedName extends string> = BaseType & {
[__brand]: BrandedName;
};

0 comments on commit 650c9b3

Please sign in to comment.