TypeScript na przestrzeni lat udowodnił że da się okiełznać dużą część problemów wynikających z dynamiczności języka JavaScript. Strukturalne typowanie które oferuje potrafi w miarę nieinwazyjnie pomóc w wykrywaniu pułapek, w które wpadlibyśmy, pisząc w czystym JS. Co jednak w przypadku gdy coś kwacze jak kaczka ale nią nie jest? Czy da się zabezpieczyć developera przed pomieszaniem dwóch różnych jednostek, które w historii doprowadziły do nie jednej katastrofy? Na prelekcji przejdziemy przez różne case study i zastanowimy się jak pomóc TypeScriptowi w ostrzeganiu nas przed pułapkami których nie zawsze da się uniknąć w pierwotnym typowaniu strukturalnym.
25. class Book {
constructor(
public id: string,
public name: string,
public isbn?: string
) {}
}
class User {
favoriteBooks: Book[] = [];
constructor(
public id: string,
public name: string,
public email: string
) {}
addFavoriteBook(book: Book) {
this.favoriteBooks.push(book);
}
}
26. class Book {
constructor(
public id: string,
public name: string,
public isbn?: string
) {}
}
class User {
favoriteBooks: Book[] = [];
constructor(
public id: string,
public name: string,
public email: string
) {}
addFavoriteBook(book: Book) {
this.favoriteBooks.push(book);
}
}
const user1 = new User("123-123-123", "John Doe", "john@doe.com");
const someBook = new Book("12345", "JavaScript: The Good Parts");
27. const user1 = new User("123-123-123", "John Doe", "john@doe.com");
const someBook = new Book("12345", "JavaScript: The Good Parts");
28. const user1 = new User("123-123-123", "John Doe", "john@doe.com");
const someBook = new Book("12345", "JavaScript: The Good Parts");
29. const user1 = new User("123-123-123", "John Doe", "john@doe.com");
const someBook = new Book("12345", "JavaScript: The Good Parts");
user1.addFavoriteBook(
30. const user1 = new User("123-123-123", "John Doe", "john@doe.com");
const someBook = new Book("12345", "JavaScript: The Good Parts");
user1.addFavoriteBook(s
s
s
s
s
s
s
s
);
31. const user1 = new User("123-123-123", "John Doe", "john@doe.com");
const someBook = new Book("12345", "JavaScript: The Good Parts");
user1.addFavoriteBook(s
s
s
s
s
s
s
s
);
✅
32. const user1 = new User("123-123-123", "John Doe", "john@doe.com");
const someBook = new Book("12345", "JavaScript: The Good Parts");
user1.addFavoriteBook(s
s
s
s
s
s
s
s
);
33. const user1 = new User("123-123-123", "John Doe", "john@doe.com");
const someBook = new Book("12345", "JavaScript: The Good Parts");
user1.addFavoriteBook(
34. const user1 = new User("123-123-123", "John Doe", "john@doe.com");
const someBook = new Book("12345", "JavaScript: The Good Parts");
user1.addFavoriteBook("12345");
35. const user1 = new User("123-123-123", "John Doe", "john@doe.com");
const someBook = new Book("12345", "JavaScript: The Good Parts");
user1.addFavoriteBook(3.14);
36. const user1 = new User("123-123-123", "John Doe", "john@doe.com");
const someBook = new Book("12345", "JavaScript: The Good Parts");
user1.addFavoriteBook(undefined);
37. const user1 = new User("123-123-123", "John Doe", "john@doe.com");
const someBook = new Book("12345", "JavaScript: The Good Parts");
user1.addFavoriteBook(z
z
z
z
z
);
38. const user1 = new User("123-123-123", "John Doe", "john@doe.com");
const someBook = new Book("12345", "JavaScript: The Good Parts");
user1.addFavoriteBook(z
z
z
z
z
);
39. const user1 = new User("123-123-123", "John Doe", "john@doe.com");
const someBook = new Book("12345", "JavaScript: The Good Parts");
user1.addFavoriteBook(z
z
z
z
z
);
40. const user1 = new User("123-123-123", "John Doe", "john@doe.com");
const someBook = new Book("12345", "JavaScript: The Good Parts");
user1.addFavoriteBook(
41. const user1 = new User("123-123-123", "John Doe", "john@doe.com");
const someBook = new Book("12345", "JavaScript: The Good Parts");
user1.addFavoriteBook(user1);
42. const user1 = new User("123-123-123", "John Doe", "john@doe.com");
const someBook = new Book("12345", "JavaScript: The Good Parts");
user1.addFavoriteBook(user1);
😬
43.
44. class Book {
constructor(
public id: string,
public name: string,
public isbn?: string
) {}
}
class User {
favoriteBooks: Book[] = [];
constructor(
public id: string,
public name: string,
public email: string
) {}
addFavoriteBook(book: Book) {
this.favoriteBooks.push(book);
}
}
45. class User {
favoriteBooks: Book[] = [];
constructor(
public id: string,
public name: string,
public email: string
) {}
addFavoriteBook(book: Book) {
this.favoriteBooks.push(book);
}
}
type Book = {
id: string;
name: string;
isbn?: string;
}
46. type Book = {
id: string;
name: string;
isbn?: string;
}
type User = {
id: string;
name: string;
email: string;
favoriteBooks: Book[];
addFavoriteBook: (book: Book) => void;
}
47. type Book = {
id: string;
name: string;
isbn?: string;
}
type User = {
id: string;
name: string;
email: string;
favoriteBooks: Book[];
addFavoriteBook: (book: Book) => void;
}
Book
48. type Book = {
id: string;
name: string;
isbn?: string;
}
type User = {
id: string;
name: string;
email: string;
favoriteBooks: Book[];
addFavoriteBook: (book: Book) => void;
}
Book User
49. type Book = {
id: string;
name: string;
isbn?: string;
}
type User = {
id: string;
name: string;
email: string;
favoriteBooks: Book[];
addFavoriteBook: (book: Book) => void;
}
Book User
50. type Book = {
id: string;
name: string;
isbn?: string;
}
type User = {
id: string;
name: string;
email: string;
favoriteBooks: Book[];
addFavoriteBook: (book: Book) => void;
}
Book User
51. type Book = {
id: string;
name: string;
isbn?: string;
}
type User = {
id: string;
name: string;
email: string;
favoriteBooks: Book[];
addFavoriteBook: (book: Book) => void;
}
Book User
52. type Book = {
id: string;
name: string;
isbn?: string;
}
type User = {
id: string;
name: string;
email: string;
favoriteBooks: Book[];
addFavoriteBook: (book: Book) => void;
}
Book User
53. type Book = {
id: string;
name: string;
isbn?: string;
}
type User = {
id: string;
name: string;
email: string;
favoriteBooks: Book[];
addFavoriteBook: (book: Book) => void;
}
Book ⊄ User
54. type Book = {
id: string;
name: string;
isbn?: string;
}
type User = {
id: string;
name: string;
email: string;
favoriteBooks: Book[];
addFavoriteBook: (book: Book) => void;
}
Book User
55. type Book = {
id: string;
name: string;
isbn?: string;
}
type User = {
id: string;
name: string;
email: string;
favoriteBooks: Book[];
addFavoriteBook: (book: Book) => void;
}
Book
User
56. class Book {
constructor(
public id: string,
public name: string,
public isbn?: string
) {}
}
class User {
favoriteBooks: Book[] = [];
constructor(
public id: string,
public name: string,
public email: string
) {}
addFavoriteBook(book: Book) {
this.favoriteBooks.push(book);
}
}
type Book = {
id: string;
name: string;
isbn?: string;
}
type User = {
id: string;
name: string;
email: string;
favoriteBooks: Book[];
addFavoriteBook: (book: Book) => void;
}
User Book
57. class Book {
constructor(
public id: string,
public name: string,
public isbn?: string
) {}
}
class User {
favoriteBooks: Book[] = [];
constructor(
public id: string,
public name: string,
public email: string
) {}
addFavoriteBook(book: Book) {
this.favoriteBooks.push(book);
}
}
type Book = {
id: string;
name: string;
isbn?: string;
}
type User = {
id: string;
name: string;
email: string;
favoriteBooks: Book[];
addFavoriteBook: (book: Book) => void;
}
User Book
58. class Book {
constructor(
public id: string,
public name: string,
public isbn?: string
) {}
}
class User {
favoriteBooks: Book[] = [];
constructor(
public id: string,
public name: string,
public email: string
) {}
addFavoriteBook(book: Book) {
this.favoriteBooks.push(book);
}
}
type Book = {
id: string;
name: string;
isbn?: string;
}
type User = {
id: string;
name: string;
email: string;
favoriteBooks: Book[];
addFavoriteBook: (book: Book) => void;
}
User Book
59. class Book {
constructor(
public id: string,
public name: string,
public isbn?: string
) {}
}
class User {
favoriteBooks: Book[] = [];
constructor(
public id: string,
public name: string,
public email: string
) {}
addFavoriteBook(book: Book) {
this.favoriteBooks.push(book);
}
}
type Book = {
id: string;
name: string;
isbn?: string;
}
type User = {
id: string;
name: string;
email: string;
favoriteBooks: Book[];
addFavoriteBook: (book: Book) => void;
}
User Book
60. class Book {
constructor(
public id: string,
public name: string,
public isbn?: string
) {}
}
class User {
favoriteBooks: Book[] = [];
constructor(
public id: string,
public name: string,
public email: string
) {}
addFavoriteBook(book: Book) {
this.favoriteBooks.push(book);
}
}
type Book = {
id: string;
name: string;
isbn?: string;
}
type User = {
id: string;
name: string;
email: string;
favoriteBooks: Book[];
addFavoriteBook: (book: Book) => void;
}
User ⊂ Book
61. “if it looks like a duck, swims like a
duck, and quacks like a duck, then it
probably is a duck”
Duck typing
62. “if it looks like a duck, swims like a
duck, and quacks like a duck, then it
probably is a duck”
Duck typing
~JavaScript
107. class Money {
constructor(
public amount: number,
public currency: string,
) {}
}
add(money: Money): Money {
return new Money(this.amount + money.amount, this.currency);
}
108. class Money {
constructor(
public amount: number,
public currency: string,
) {}
}
add(money: Money): Money {
if (this.currency !== money.currency) {
throw new Error("Cannot add different currencies");
}
return new Money(this.amount + money.amount, this.currency);
}
109. class Money<T extends string> {
constructor(
public amount: number,
public currency: T,
) {}
add(money: Money<T>): Money<T> {
return new Money(this.amount + money.amount, this.currency);
}
}
110. class Money<T extends string> {
constructor(
public amount: number,
public currency: T,
) {}
add(money: Money<T>): Money<T> {
return new Money(this.amount + money.amount, this.currency);
}
}
const somePLNs = new Money(100, 'PLN');
const someEuros = new Money(100, 'EUR');
somePLNs.add(someEuros);
111. class Money<T extends string> {
constructor(
public amount: number,
public currency: T,
) {}
add(money: Money<T>): Money<T> {
return new Money(this.amount + money.amount, this.currency);
}
}
const somePLNs = new Money(100, 'PLN');
const someEuros = new Money(100, 'EUR');
somePLNs.add(someEuros);
112. class Money
constructor(
public amount: number,
public currency: T,
) {}
add(money: Money<T>): Money<T> {
return new Money(this.amount + money.amount, this.currency);
}
}
const somePLNs: Money<string> = new Money(100, 'PLN');
const someEuros = new Money(100, 'EUR');
somePLNs.add(someEuros);
<T e
e
e
e
e
e
e
e
e
e
e
e
e
>
>
113. class Money
constructor(
public amount: number,
public currency: T,
) {}
add(money: Money<T>): Money<T> {
return new Money(this.amount + money.amount, this.currency);
}
}
const somePLNs: Money<string> = new Money(100, 'PLN');
const someEuros = new Money(100, 'EUR');
somePLNs.add(someEuros);
114. class Money
constructor(
public amount: number,
public currency: T,
) {}
add(money: Money<T>): Money<T> {
return new Money(this.amount + money.amount, this.currency);
}
}
const somePLNs: Money<string> = new Money(100, 'PLN');
const someEuros = new Money(100, 'EUR');
somePLNs.add(someEuros);
<in out T extends string> {
115. class Money
constructor(
public amount: number,
public currency: T,
) {}
add(money: Money<T>): Money<T> {
return new Money(this.amount + money.amount, this.currency);
}
}
const somePLNs: Money<string> = new Money(100, 'PLN');
const someEuros = new Money(100, 'EUR');
somePLNs.add(someEuros);
<in out T extends string> {
116. class Money
constructor(
public amount: number,
public currency: T,
) {}
add(money: Money<T>): Money<T> {
return new Money(this.amount + money.amount, this.currency);
}
}
const somePLNs: Money<string> = new Money(100, 'PLN');
const someEuros = new Money(100, 'EUR');
somePLNs.add(someEuros);
<in out T extends string> {
117. class Money
constructor(
public amount: number,
public currency: T,
) {}
add(money: Money<T>): Money<T> {
return new Money(this.amount + money.amount, this.currency);
}
}
const somePLNs = new Money(100, 'PLN' as string);
const someEuros = new Money(100, 'EUR' as string);
somePLNs.add(someEuros);
<T extends string> {
<in out T extends string> {
210. Możemy dodać prymitywnym typom nominały
type UserId = NominalType<string, 'user_id'>
type BookId = NominalType<string, 'book_id'>
211. Case study z pieniędzmi to być może nie najlepsze zastosowanie,
212. Case study z pieniędzmi to być może nie najlepsze zastosowanie,
ale warto wiedzieć że w TS istnieje masa sztuczek ;-)
213. Case study z pieniędzmi to być może nie najlepsze zastosowanie,
ale warto wiedzieć że w TS istnieje masa sztuczek ;-)
Enkapsulacja operacji na prymitywnych typach
214. Case study z pieniędzmi to być może nie najlepsze zastosowanie,
ale warto wiedzieć że w TS istnieje masa sztuczek ;-)
Enkapsulacja operacji na prymitywnych typach
Opaque types
216. Typy nominalne mogą czuwać nad liczbami i jednostkami
async function delayedHelloWorld() {
await delay(seconds(1));
console.log('Hello World');
}
217. Typy nominalne mogą czuwać nad liczbami i jednostkami
async function delayedHelloWorld() {
await delay(seconds(1));
console.log('Hello World');
}
Prymitywne typy pod spodem
218. Typy nominalne mogą czuwać nad liczbami i jednostkami
async function delayedHelloWorld() {
await delay(seconds(1));
console.log('Hello World');
}
Prymitywne typy pod spodem
Terser?
219. Typy nominalne mogą czuwać nad liczbami i jednostkami
async function delayedHelloWorld() {
await delay(seconds(1));
console.log('Hello World');
}
Prymitywne typy pod spodem
Terser?
Inlining przy kompilacji
220. Typy nominalne mogą czuwać nad liczbami i jednostkami
async function delayedHelloWorld() {
await delay(seconds(1));
console.log('Hello World');
}
Prymitywne typy pod spodem
Terser?
Inlining przy kompilacji
async function delayedHelloWorld() {
await delay(1000);
console.log('Hello World');
}