The Typescript Book
Notes on how to use the Typescript language
How to create a frontend development environment for Typescript
from node:alpine
workdir /usr/application
cmd ["npm", "start"]
copy package.json .
run npm install
copy . .
How to create a backend development environment for Typescript
FROM node:alpine
WORKDIR /usr/server
CMD ["npm", "run", "development"]
COPY package.json .
RUN npm install
COPY . .
Three cases where you need explicit type annotation
As a rule you should rely on type inference. In 3 specific cases you need to use explicit type annotation.
// When function that returns the "any" type e.g. JSON.parse()
// interface Point {
// x: number
// y: number
// }
// const thePoint: Point = { x: 10, y: 20 }
const thePoint = { x: 10, y: 20 }
const coordinates = JSON.parse(JSON.stringify(thePoint))
// Solution#1 const coordinates = JSON.parse(JSON.stringify(thePoint)) as Point
// Solution#2 const coordinates: Point = JSON.parse(JSON.stringify(thePoint))
console.log(coordinates)
How to annotate a function
// Case 1: Annotate variable
const sum
: (a: number, b: number) => number
= (a, b) => a + b
How to annotate an object
const person
: { name: string, age: number,
coordinates: { longitude: number, latitude: number }}
= { name: 'bob', age: 35,
coordinates: { longitude: 1.23456, latitude: 6.54321 }}
How to annotate a destructured value
const todaysWeather = {
date: new Date(),
weather: 'sunny',
}
// Case 1: parameter is not destructured
const logWeather1 = (forecast: { date: Date; weather: string }): void => {
console.log(forecast.date)
console.log(forecast.weather)
}
logWeather1(todaysWeather)
How to annotate arrays
// homogenous arrays
const array: string[] = []
array.push('')
const value = array.pop() // value inferred to be of type string
How to annotate tuples
// A tuple is an array that expects specific types at specific indexes
// The code below shows how to use an enumeration to enforce this ordering
// define enumeration to provide access to tuple elements at specific indicies
enum Quality { color, fizzy, calories }
const { color, fizzy, calories } = Quality
// define the type of the tuple
type Drink = [string, boolean, number]
// define an instance of a tuple
const pepsi: Drink = ['brown', true, 40]
pepsi[color] = 40 // error - cannot assign number to string
pepsi[fizzy] = 'brown' // error - cannot assign string to boolean
pepsi[calories] = true // error - cannot assign boolean to number
How to create types in Typescript
// (1) create a type for a function
type predicate = (value: number) => boolean
const isEven: predicate = (value) => value % 2 === 0
// (2) create a type for an object
type Person = { name: string; age: number }
const robert: Person = { name: 'bob', age: 35 }
// (3) create a type for a tuple
type Beverage = [string, boolean, number]
const drPepper: Beverage = ['brown', true, 75]
// (4) create a stype for a heterogenous array
type Dates = (string | Date)[]
const rolodex: Dates = ['2020-09-25 10:00:05', new Date()]
How to create interfaces in Typescript
interface Reportable {
name: string
year: number
broken: boolean
print(): void
}
const car = {
name: 'civic',
year: 2000,
broken: true,
print() {
console.log(`Name: ${this.name}`)
console.log(`Year: ${this.year}`)
console.log(`Broken: ${this.broken}`)
},
}
const printVehicle = (vehicle: Vehicle) => {
vehicle.print()
}
printVehicle(car)
How to add method and property accessibility specifiers
class Vehicle {
// only callable within class and subclasses
protected drive() {
console.log('chugga-chugga!')
}
// callable by anyone on class instance or subclass instance
public honk() {
console.log('beep-beep!')
}
// callable only within class
private price() {
console.log('$1200')
}
}
class Car extends Vehicle {
// visibilty of protected method can be increased when overriding
public drive() {
console.log('vroom-vroom!')
}
}
const car = new Car()
console.log(car.drive())
console.log(car.honk())
How to use generics with constraints
class Printer<T> {
constructor(private valueOne: T) {}
print(valueTwo: T): T {
console.log(valueTwo)
return this.valueOne
}
}
const printer = new Printer<number>(42)
printer.print(24)
// printer.print('invalid value')
How to use decorators
class Boat {
@log('I', 'could')
pilot(): void {
throw new Error('not pilot boat')
}
}
function log(a: string, b: string) {
return function (target: any, key: string, descriptor: PropertyDescriptor) {
const method = descriptor.value
descriptor.value = function () {
try {
method()
} catch (error) {
console.log(`${a} ${b} ${error.message}`)
}
}
}
}
const boat = new Boat()
boat.pilot() // prints the message "I could not pilot boat"
How to use React prop types with Typescript
import React, { Component } from 'react'
import ReactDOM from 'react-dom'
// (1) These are where you define the prop types for your component
interface AppProps {
// (2) Using JSDOC comments show up in Visual Code on hover 🥰
/** The text to show */
text?: string
}
class App extends Component<AppProps> {
// (3) These are your default prop values
static defaultProps = {
text: 'Not Available',
}
render() {
return <h1>{this.props.text}</h1>
}
}
ReactDOM.render(<App />, document.getElementById('root'))
How to use useRef instead of useState to represent a form
The usual approach for a form is to use state and set up change handlers on each input field. However this results on change handlers being fired on every keypress, potentially a large number of events. Since we are only interested in the contents of the input fields on a form submission, we can use refs to the input fields and only grab their values once when there is a form submission.
import {
FormEvent,
FormEventHandler,
FunctionComponent,
useRef,
RefObject,
} from 'react'
const getAndReset = (ref: RefObject<HTMLInputElement>): string => {
if (!ref.current) return ''
const value = ref.current.value
ref.current.value = ''
return value
}
export const SignIn: FunctionComponent = (): JSX.Element => {
const emailRef = useRef<HTMLInputElement>(null)
const passwordRef = useRef<HTMLInputElement>(null)
const handleSubmit: FormEventHandler = (
event: FormEvent<HTMLFormElement>
): void => {
event.preventDefault()
const email = getAndReset(emailRef)
const password = getAndReset(passwordRef)
console.log('form submission', email, password)
}
return (
<div className="sign-in">
<h2>I already have an account</h2>
<span>Sign in with your email and password</span>
<form onSubmit={handleSubmit}>
<input name="email" type="email" required ref={emailRef} />
<label htmlFor="email">Email</label>
<input type="password" name="password" ref={passwordRef} />
<label htmlFor="password">Password</label>
<input name="submit" type="submit" value="Submit Form" />
</form>
</div>
)
}
Last updated