Gion Kunz
Let's create together.
by Gion Kunz
Founder, UI Developer, UX Enthusiast at syncrea GmbH
M82 (Bode's Galaxy, left) and M81 (Cigar Galaxy, right), both about 12 Mio Lightyears away.
TypeScript Base
No Implicit Any
Any and Unknown
Strict null checks
Type Declarations
Use native types
Declaration by initialization
Tuple type declarations
Literal types
Union types (and & or)
String literal union types
ECMAScript 2015 - 2018
Modules
Var vs. Let vs. Const
Template Strings
Loops (for, for of, forEach)
Arrow Functions
Destructuring
Spreading
async / await
Functional Primer
Pure functions
Immutability
OOP > FP > Pragmatic TypeScript
Object literals with Interfaces
The Myth of OOP
Advanced Topics
Type aliases for Documentation
Discriminated Union Types
The Partial Interface
The keyof Type
Conditional Generic Types
User Defined Type Guards with type predicates
// tsconfig.json
{
"compilerOptions": {
"noImplicitAny": true
}
}
let x: any = 10;
x = 'anything';
let y: unknown = 5;
y = 'unknown';
let z: number;
z = x;
// Not possible!
z = y;
// tsconfig.json
{
"compilerOptions": {
"noImplicitAny": true,
"strictNullChecks": true
}
}
let x: string | null;
x!.toLowerCase();
function die(message: string): never {
throw new Error(message);
}
(x || die('Ooops...')).toLowerCase();
let n: number;
// Not this -> let n: Number;
let b: boolean;
// Not this -> let b: Boolean;
let s: string;
// Not this -> let s: String;
const n = 100;
// Not this -> const n: number = 100;
const x = 'Hello World';
// Not this -> const x: string = 'Hello World';
type NameAgeTuple = [string, number];
const nameAndAge: NameAgeTuple = ['Peter', 52];
type AnswerToAllQuestions = 42;
// Error!
const n: AnswerToAllQuestions = 13;
type FibonacciNumber = 1 | 2 | 3 | 5 | 8 | 13;
// Error!
const n: FibonacciNumber = 4;
type OrderStatus = 'Ordered' | 'Paid' | 'Shipped' | 'Delivered';
interface Order {
id: number;
status: OrderStatus;
}
const order: Order = {
id: 1,
status: 'Ordered'
};
interface Cat {
name: string;
meow: () => void;
}
interface Dog {
name: string;
bark: () => void;
}
export type CatDog = Cat & Dog;
// a.ts
const magicNumber = 13;
export const modulePublic = 'This is public';
export function multiplyWithMagicNumber(n: number): number {
return magicNumber * n;
}
// Default export
export default [1, 2, 3, 5, 8, 13];
// index.ts
import {
modulePublic as moduleAText,
multiplyWithMagicNumber
} from './a';
import fibonacci from './a';
import * as A from './a';
console.log(moduleAText);
console.log(multiplyWithMagicNumber(7));
console.log(fibonacci);
console.log(A.default === fibonacci);
console.log(A.modulePublic === moduleAText);
function closure() {
var closureScoped = 1;
for(let i = 0; i < 10; i++) {
}
// Error
console.log(i);
}
const peter = {
name: 'Peter'
};
console.log(`My name is ${peter.name}!`);
const list = [1, 2, 3, 4, 5, 6, 7];
// Traditional but block scope counter
for(let i = 0; i < list.length; i++) {
console.log(i);
}
// Functional
list.forEach(console.log);
// for...of loop
for(let item of list) {
console.log(item);
}
const multiply = (a: number, b: number) => a * b;
// Array destructuring
const list = [1, 2, 3];
const [first, second, third] = list;
// Object destructuring
const peter = {
firstName: 'Peter',
lastName: 'Griffin'
};
const {firstName, lastName} = peter;
export interface ConfigurationOptions {
delay: number;
host: string;
limit: number;
}
function configure({delay = 1000,
host = 'localhost',
limit = 300}: Partial<ConfigurationOptions>) {
console.log(host);
console.log(delay);
console.log(limit);
}
// Array spreading
const list = [1, 2, 3, 4];
const appended = [...list, 5, 6, 7];
// Object property spreading
const peter = {
firstName: 'Peter',
lastName: 'Griffin'
};
const peterWithAge = {
...peter,
age: 52
};
// Traditional promise chain
function invalidateUsersLame() {
return fetch('/api/users')
.then(usersResponse => usersResponse.json())
.then((users: User[]) =>
Promise.all(users.map(user =>
fetch(`/api/users/${user.id}/invalidate`).then(() => user)
))
);
}
// Async await löve!
async function invalidateUsers() {
const usersResponse = await fetch('/api/users');
const users: User[] = await usersResponse.json();
for(let user of users) {
await fetch(`/api/users/${user.id}/invalidate`);
}
return users;
}
// Pure
function multiply(a: number, b: number): number {
return a * b;
}
// Impure
const name = 'Peter';
function getNameLength() {
return name.length;
}
interface Person {
readonly name: string;
readonly age: number;
}
const person: Person = {
name: 'Peter',
age: 52
};
// Error!
person.name = 'Pete';
interface Person {
name: string;
age: number;
}
const person: Readonly<Person> = {
name: 'Peter',
age: 52
};
// Error!
person.name = 'Pete';
const list: ReadonlyArray<number> = [1, 2, 3, 4];
// Error!
list[0] = 10;
// Error!
list.sort();
// Error!
list.fill(42);
export interface Person {
firstName: string;
lastName: string;
}
const person: Person = {
firstName: 'Peter',
lastName: 'Griffin'
};
export class DesigningDeveloper {
design(): void {
// Can design
}
develop(): void {
// Can develop
}
}
export type PersonId = number;
function getPerson(id: PersonId) {
}
export interface Apple {
readonly kind: 'Apple';
readonly color: string;
}
export interface Banana {
readonly kind: 'Banana';
readonly bendRadius: number;
}
export type Fruit = Apple | Banana;
export interface Person {
readonly name: string;
readonly age: number;
readonly isHungry: boolean;
}
function updatePerson(data: Partial<Person>) {
// Do stuff with partial person data...
}
export interface Person {
readonly name: string;
readonly age: number;
readonly isHungry: boolean;
}
export type PersonKey = keyof Person;
// First version... We can do better!
function getPropertyLame<T>(object: T, key: keyof T) {
return object[key];
}
// That's fantastic!
function getProperty<T, K extends keyof T>(object: T, key: K): T[K] {
return object[key];
}
export type Partial<T> = {
[K in keyof T]?: T[K];
};
export type YesNo<T extends boolean> =
T extends true ? 'Yes' : 'No';
export type Unpacked<T> =
T extends (infer A)[] ? A :
T extends Promise<infer P> ? P :
T extends Set<infer S> ? S :
T extends Map<infer MK, infer MV> ? [MK, MV] :
T;
By Gion Kunz
Slides for our Pragmatic TypeScript Workshop