---
# System prepended metadata

title: TypeScript
tags: [typescript]

---

# TypeScript

###### tags: `typescript`

## What is TypeScript

TypeScript is an **syntactic superset** of JavaScript.
The Goal is add **Types** into JavaScript.

- Compiles to readable JS
- Three parts: Language, Language Server and Compiler
- Kind of like a fancy linter

## Why need Types

```javascript=
function add(a, b) {
    return a + b;
}
```

```javascript=
function add(a, b, c = 0) {
    return a + b + c;
}
```

### Types make the author's intent more clear

```typescript=
function add(a: number, b: number): number {
    return a + b;
}
```

### Move some kinds of errors from runtime to compile time.

- Values that are null or undefined
- Incomplete refactoring
- Breakage around internal code contracts (e.g. an argument becomes required)

### Great code authoring experience

## TSC compiler

```json=
{
    "devDependencies": {
        "typescript": "^4.3.2"
    },
    "scripts": {
        "dev": "tsc --watch --preserveWatchOutput"
    }
}
```


```json=5
{
    "compilerOptions": {
        "outDir": "dist",
        "target": "ES3"
    },
    "include": ["src"]
}
```

```json=
{
    "compilerOptions": {
        "outDir": "dist",
        "target": "ES2015"
    },
    "include": ["src"]
}
```

```json=
{
    "compilerOptions": {
        "outDir": "dist",
        "target": "ES2017"
    },
    "include": ["src"]
}
```

Legacy Browser support pipeline: Typescript ES2017 => Babal

`.d.ts` known as a declaration file.

```typescript=
/**
 * Add three numbers
 * @param a first number
 * @param b second
 */
export declare function addNumbers(a: number, b: number): Promise<number>;
```

A good way to think of TS files:
- `.ts` files contain both type information and code
- `.js` files contain only code
- `.d.ts` files contain only type information

## Variables and Values

```typescript=
let age = 6;
age = "not a number";
```

TypeScript is able to infer that `age` is a number,
based on the value as we are declaring.

In TypeScript, variables are "born" with their types.

```typescript=
const age = 6;
```

TS is able to make a more specific:

- `const` variable declarations cannot be reassigned
- the initial value assigned to `age` is a number, which is an immutable value.

### Literal Types

The type `6` is called a **literal type**.

### Implicit `any` and type annotations

```typescript=
const RANDOM_WAIT_TIME = Math.round(Math.random() * 500) + 500;

let startTime = new Date();
let endTime;

setTimeout(() => {
    endTime = 0;
    endTime = new Date();
}, RANDOM_WAIT_TIME);
```

`endTime` is "born" without a type, so it ends up being an implicit `any`.

If we wanted more safety here, we could be add a **type annotation**:

```typescript=
const RANDOM_WAIT_TIME = Math.round(Math.random() * 500) + 500;

let startTime = new Date();
let endTime: Date;

setTimeout(() => {
    // endTime = 0;
    endTime = new Date();
}, RANDOM_WAIT_TIME);
```

## Function arguments and return values

```typescript=
function add(a: number, b: number): number {}
```

This is great way for code authors to state their intentions up-front.
**explicit return type**

**TypeScript is not a replacement for unit test**

## Objects

Object types are defined by:

- The `names` of the properties that are (or may be) present.
- The `types` of those properties

```typescript=
let car = {
  make: "Toyota",
  model: "Corolla",
  year: 2002
}
```

```typescript=
{
  make: string,
  model: string,
  year: number
}
```

### Optional Properties

```typescript=
{
  make: string,
  model: string,
  year: number,
  chargeVoltage?: number
}
```

```typescript=
function printCar(car: {
    make: string,
    model: string,
    year: number,
    chargeVoltage?: number
}) {
    let str = `${car.make} ${car.model} ${car.year}`;
    
    if (typeof car.chargeVoltage !== 'undefined') {
        str += `// ${car.chargeVoltage}`;
    }
    
    console.log(str);
}
```

**Compare with this**

```typescript=
{
  make: string,
  model: string,
  year: number,
  chargeVoltage: number | undefined
}
```

## Excess property checking

```typescript=
function printCar(car: {
    make: string,
    model: string,
    year: number,
    chargeVoltage?: number
}) {
}

printCar({
    make: "Tesla",
    model: "Model 3",
    year: 2020,
    chargeVoltage: 220,
    color: "RED" // <==== Extra Property
})
```

**Compare with this**
```typescript=
function printCar(car: {
    make: string,
    model: string,
    year: number,
    chargeVoltage?: number
}) {
}

const myCar = {
    make: "Tesla",
    model: "Model 3",
    year: 2020,
    chargeVoltage: 220,
    color: "RED" // <==== Extra Property
};

printCar(myCar);
```

### Index signatures

```typescript=
{
    [k: string]: {
        country: string,
        area: string,
        number: string
    }
}
```

### Array

```typescript=
const fileExtensions = ["js", "ts"]; // => string[]
```

### Tuples

```typescript=
let myCar = [2002, "Toyota", "Corolla"]; // => (string | number)[]

const [year, make, model] = myCar;
```

**Compare to**

```typescript=
let myCar: [number, string, string] = [2002, "Toyota", "Corolla"];

const [year, make, model] = myCar;
```

**Consider**

```typescript=
const numPair: [number, number] = [4, 5, 6]; // Error
```

```typescript=
const numPair: [number, number] = [4, 5];
numPair.push(6);
numPair.pop();
numPair.pop();
numPair.pop();
```

## Structural vs Nominal Types

### What is type checking?

attempts to asked the question of compatibility or type equivalence

```typescript=
function foo(x) {

}

// TYPE CHECKING
// Is `myValue` type-equivalent to what `foo` want to receive?
foo(myValue)
```

### Static vs Dynamic

type-checking is performed at **compile time** or **runtime**

**TypeScript's type system is static**

### Nominal vs structural

Nominal type systems are care about NAMES. Such as `JAVA`

Structural type systems are care about SHAPES. Such as `TypeScript`

```typescript=
class Car {
  make: string
  model: string
  year: number
  isElectric: boolean
}

class Truck {
  make: string
  model: string
  year: number
  towingCapacity: number
}

const vehicle = {
  make: "Honda",
  model: "Accord",
  year: 2017,
}

function printCar(car: {
  make: string
  model: string
  year: number
}) {
  console.log(`${car.make} ${car.model} (${car.year})`)
}

printCar(new Car()) // Fine
printCar(new Truck()) // Fine
printCar(vehicle) // Fine
```

### Duck Typing

*Duck typing gets its name from the "duck test"*

"Duck typing" is usually used to describe dynamic type systems.

## Union and Intersection

Union types can be described using the `|` (pipe) operator

```typescript=
function flipCoin(): "heads" | "tails" {
  if (Math.random() > 0.5) return "heads";
  
  return "tails";
}

const outcome = flipCoin();
```

```typescript=
function flipCoin(): "heads" | "tails" {
  if (Math.random() > 0.5) return "heads"
  return "tails"
}

function maybeGetUserInfo():
  | ["error", Error]
  | ["success", { name: string; email: string }] {
  if (flipCoin() === "heads") {
    return [
      "success",
      { name: "Mike North", email: "mike@example.com" },
    ];
  } else {
    return [
      "error",
      new Error("The coin landed on TAILS :("),
    ];
  }
}

const outcome = maybeGetUserInfo();
```

### Narrowing with type guards

Type guards are expressions, which when used with control flow statement,
allow us to have a more specific type for a particular value.

```typescript=
const outcome = maybeGetUserInfo();
const [first, second] = outcome;
                
if (second instanceof Error) {
  // In this branch of your code, second is an Error
  second
} else {
  // In this branch of your code, second is the user info
  second
}
```

### Discriminated Unions

```typescript=
const outcome = maybeGetUserInfo();

if (outcome[0] === "error") {
  // In this branch of your code, second is an Error
  outcome
} else {
  // In this branch of your code, second is the user info
  outcome
}
```

### Intersection Types

Intersection types in TypeScript can be described using the `&` (ampersand) operator.

```typescript=
function makeWeek(): Date & { end: Date } {
  const start = new Date();
  const end = new Date(start.valueOf() + ONE_WEEK);

  return { ...start, end };
}

const thisWeek = makeWeek();
thisWeek.toISOString();

thisWeek.end.toISOString();
```

## Interfaces and Type Aliases

### Type Aliases

- define a more meaningful name for the type
- declare the particulars of the type in a single place
- import and export this type from modules

Using `TitleCase` to format the alias' name.

```typescript=
export type UserContactInfo = {
    name: string,
    email: string
}
```

We can only declare an alias of a given name once within a given scope.

```typescript=
type UserContactInfo = {

}

// Error
type UserContactInfo = {

}
```

```typescript=
type UserInfoOutcomeError = ["error", Error]
type UserInfoOutcomeSuccess = [
  "success",
  { name: string; email: string }
]
type UserInfoOutcome =
  | UserInfoOutcomeError
  | UserInfoOutcomeSuccess

export function maybeGetUserInfo(): UserInfoOutcome {
  // implementation is the same in both examples
  if (Math.random() > 0.5) {
    return [
      "success",
      { name: "Mike North", email: "mike@example.com" },
    ]
  } else {
    return [
      "error",
      new Error("The coin landed on TAILS :("),
    ]
  }
}
```

### Inheritance

create type aliases that combine existing types with new behaviour
by using Intersection (&) types.

```typescript=
type SpecialDate = Date & { getReason(): string }

const newYearsEve: SpecialDate = {
  ...new Date(),
  getReason: () => "Last day of the year",
}
newYearsEve.getReason
```

### Interfaces

An interface is a way of defining an object type.
An "object type" can be thought of as, 
"an instance of a class could conceivably look like this".

```typescript=
interface UserInfo {
  name: string
  email: string
}
```

### Inheritance

**EXTENDS**

```typescript=
interface Animal {
  isAlive(): boolean
}

interface Mammal extends Animal {
  getFurOrHairColor(): string
}

interface Dog extends Mammal {
  getBreed(): string
}

function careForDog(dog: Dog) {
  dog.getBreed
}
```

**IMPLEMENTS**

```typescript=
class LivingOrganism {
  isAlive() {
    return true
  }
}
interface AnimalLike {
  eat(food): void
}
interface CanBark {
  bark(): string
}

class Dog
  extends LivingOrganism
  implements AnimalLike, CanBark
{
  bark() {
    return "woof"
  }
  eat(food) {
    consumeFood(food)
  }
}
```

### Open Interfaces

```typescript=
interface AnimalLike {
  isAlive(): boolean
}
function feed(animal: AnimalLike) {
  animal.eat
         
  animal.isAlive
}

interface AnimalLike {
  eat(food): void
}
```

```typescript=
window.document // an existing property
          
window.exampleProperty = 42
             
interface Window {
  exampleProperty: number
}
```

### Recursion Types

```typescript=
type NestedNumbers = number | NestedNumbers[]

const val: NestedNumbers = [3, 4, [5, 6, [7], 59], 221];
```

## Functions

### Callable Types

Both type aliases and interfaces offer the capability to describe call signatures

```typescript=
interface TwoNumberCalculation {
    (x: number, y: number): number;
}

type TwoNumberCalc = (x: number, y: number) => number;

const add: TwoNumberCalculation = (a, b) => a + b;
const subtract: TwoNumberCalc = (x, y) => x - y;
```

### Function overloads

```typescript=
type FormSubmitHandler = (data: FormData) => void
type MessageHandler = (evt: MessageEvent) => void

function handleMainEvent(
  elem: HTMLFormElement,
  handler: FormSubmitHandler
)
function handleMainEvent(
  elem: HTMLIFrameElement,
  handler: MessageHandler
)
function handleMainEvent(
  elem: HTMLFormElement | HTMLIFrameElement,
  handler: FormSubmitHandler | MessageHandler
) {}

const myFrame = document.getElementsByTagName("iframe")[0]
const myForm = document.getElementsByTagName("form")[0]
        
handleMainEvent(myFrame, (val) => {
})
handleMainEvent(myForm, (val) => {
})
```

### `this` types

```typescript=
function myClickHandler(
  this: HTMLButtonElement,
  event: Event
) {
  this.disabled = true
}
```

## Classes

### Class Fields

```typescript=
class Car {
  make: string
  model: string
  year: number
  
  constructor(make: string, model: string, year: number) {
    this.make = make
    this.model = model
    this.year = year
  }
}
```

### Access modifier keywords


| keyword    | who can access                          |
| ---------- | --------------------------------------- |
| public     | everyone (this is the default)          |
| protected  | the instance itself, and subclasses     |
| private    | only the instance itself                |


```typescript=
class Car {
  public make: string
  public model: string
  public year: number
  protected vinNumber = generateVinNumber()
  private doorLockCode = generateDoorLockCode()

  constructor(make: string, model: string, year: number) {
    this.make = make
    this.model = model
    this.year = year
  }

  protected unlockAllDoors() {
    unlockCar(this, this.doorLockCode)
  }
}

class Sedan extends Car {
  constructor(make: string, model: string, year: number) {
    super(make, model, year)
    
    this.vinNumber
    this.doorLockCode
  }
  
  public unlock() {
    console.log("Unlocking at " + new Date().toISOString())
    this.unlockAllDoors()
  }
}
```

### Private

```typescript=
class Car {
  public make: string
  public model: string
  #year: number

  constructor(make: string, model: string, year: number) {
    this.make = make
    this.model = model
    this.#year = year
  }
}
```

### Readonly

```typescript=
class Car {
  public make: string
  public model: string
  public readonly year: number

  constructor(make: string, model: string, year: number) {
    this.make = make
    this.model = model
    this.year = year
  }

  updateYear() {
    this.year++
  }
}
```

### Param properties

```typescript=
class Car {
  constructor(
    public make: string,
    public model: string,
    public year: number
  ) {}
}
```

## Top and Bottom Types

### Types describe sets of allowed values

`{true, false}`.
```typescript=
const x: boolean
```

`{y | y is a number}`
```typescript=
const y: number
```

### Top Types

A top type is a type that describes 
**any possible value allowed by the system**

`{x | x could be anything}`

TypeScript provides two of these types: `any` and `unknown`

#### any

playing by the usual JavaScript rules

It's important to understand that `any` is not necessarily a problem.
sometimes it's exactly the right type to use for a particular situation.

We can see here that `any` 
is not always a "bug" or a "problem"
it just indicates maximal flexibility 
and the absence of type checking validation.

#### unknown

Like `any`, unknown can accept any value

However, `unknown` is different from `any` 
in a very important way:

**Values with an `unknown` type cannot be used without first applying a type guard**

```typescript=
let myUnknown: unknown = 14

// This code runs for { myUnknown| anything }
if (typeof myUnknown === "string") {
  // This code runs for { myUnknown| all strings }
  console.log(myUnknown, "is a string")
} else if (typeof myUnknown === "number") {
  // This code runs for { myUnknown| all numbers }
  console.log(myUnknown, "is a number")
} else {
}
```

#### Practical use of top types

if you convert a project from JavaScript to TypeScript
A lot of things will be `any` until you get a chance 
to give them some attention.

`unknown` is great for values received at runtime.
By consumers of these values 
to perform some light validation before using them,
errors are caught earlier,
and can often be surfaced with more context.

### Bottom type: `never`

A bottom type is a type that describes 
**no possible value allowed by the system**

“any value from the following set: { } (intentionally empty)”

```typescript=
class Car {
  drive() {
    console.log("vroom")
  }
}
class Truck {
  tow() {
    console.log("dragging something")
  }
}
type Vehicle = Truck | Car

let myVehicle: Vehicle = obtainRandomVehicle()

// The exhaustive conditional
if (myVehicle instanceof Truck) {
  myVehicle.tow() // Truck
} else if (myVehicle instanceof Car) {
  myVehicle.drive() // Car
} else {
  // NEITHER!
  const neverValue: never = myVehicle
}
```

**error subclass**

```typescript=
class UnreachableError extends Error {
  constructor(_nvr: never, message: string) {
    super(message)
  }
}

// The exhaustive conditional
if (myVehicle instanceof Truck) {
  myVehicle.tow() // Truck
} else if (myVehicle instanceof Car) {
  myVehicle.drive() // Car
} else {
  // NEITHER!
  throw new UnreachableError(
    myVehicle,
    `Unexpected vehicle type: ${myVehicle}`
  )
}
```

## Type Guards and narrowing

### Built-in type guards

```typescript=
let value:
  | Date
  | null
  | undefined
  | "pineapple"
  | [number]
  | { dateRange: [Date, Date] }

// instanceof
if (value instanceof Date) {
  value // Date
}
// typeof
else if (typeof value === "string") {
  value // "pineapple"
}
// Specific value check
else if (value === null) {
  value // null
}
// Truthy/falsy check
else if (!value) {
  value // undefined
}
// Some built-in functions
else if (Array.isArray(value)) {
  value // [number]
}
// Property presence check
else if ("dateRange" in value) {
  value // { dateRange: [Date, Date]; }
} else {
  value // never
}
```

### User-defined type guards

**“Untruths” in your type guards will propagate quickly through your codebase and cause problems that are quite difficult to solve.**

```typescript=
interface CarLike {
  make: string
  model: string
  year: number
}

let maybeCar: unknown

// the guard
function isCarLike(
  valueToTest: any
): valueToTest is CarLike {
  return (
    valueToTest &&
    typeof valueToTest === "object" &&
    "make" in valueToTest &&
    typeof valueToTest["make"] === "string" &&
    "model" in valueToTest &&
    typeof valueToTest["model"] === "string" &&
    "year" in valueToTest &&
    typeof valueToTest["year"] === "number"
  )
}

// using the guard
if (isCarLike(maybeCar)) {
  maybeCar
}
```

#### Asserts value is Type

```typescript=
interface CarLike {
  make: string
  model: string
  year: number
}

let maybeCar: unknown

// the guard
function assertsIsCarLike(
  valueToTest: any
): asserts valueToTest is CarLike {
  if (
    !(
      valueToTest &&
      typeof valueToTest === "object" &&
      "make" in valueToTest &&
      typeof valueToTest["make"] === "string" &&
      "model" in valueToTest &&
      typeof valueToTest["model"] === "string" &&
      "year" in valueToTest &&
      typeof valueToTest["year"] === "number"
    )
  )
    throw new Error(
      `Value does not appear to be a CarLike${valueToTest}`
    )
}
```

## Nullish

### `null`

there is a value, and that value is nothing.

### `undefined`

the value isn't available (yet)

### `void`

a function's return value should be ignored

### Non-null assertion operator

The non-null assertion operator (`!.`) 
is used to cast away the possibility 
that a value might be `null` or `undefined`.

```typescript=
type GroceryCart = {
  fruits?: { name: string; qty: number }[]
  vegetables?: { name: string; qty: number }[]
}

const cart: GroceryCart = {}

cart.fruits.push({ name: "kumkuat", qty: 1 })
cart.fruits!.push({ name: "kumkuat", qty: 1 })
```

### Definite assignment operator

```typescript=
class ThingWithAsyncSetup {
  setupPromise: Promise<any> 
  isSetup!: boolean

  constructor() {
    this.setupPromise = new Promise((resolve) => {
      this.isSetup = false
      return this.doSetup()
    }).then(() => {
      this.isSetup = true
    })
  }

  private async doSetup() {
  }
}
```

## Generics

`Generics` allow us to parameterize types

What we need here is some mechanism of defining a relationship between the type of the thing we’re passed, and the type of the thing we’ll return. This is what Generics are all about

```typescript=
function listToDict(
  list: any[],
  idGen: (arg: any) => string
): { [k: string]: any } {

  const dict: { [k: string]: any } = {}
  list.forEach((element) => {
    const dictKey = idGen(element)
    dict[dictKey] = element
  })
  return dict
}
```

### Defining a type parameter

Type parameters can be thought of as “function arguments, but for types”.


Generics may change their type, depending on the type parameters you use with them.


```typescript=
function listToDict<T>(
  list: T[],
  idGen: (arg: T) => string
): { [k: string]: T } {
  const dict: { [k: string]: T } = {}
  return dict
}
```

### Generic Constraints

Generic constraints allow us to describe the “minimum requirement” for a type param

```typescript=
function listToDict<T extends HasId>(list: T[]): Dict<T> {
}
```

### Scopes and TypeParams

```typescript=
// outer function
function tupleCreator<T>(first: T) {
  // inner function
  return function finish<S>(last: S): [T, S] {
    return [first, last]
  }
}
```




<style>
    .markdown-body pre {
        background-color: #1E1E1E;
        border: 1px solid #555 !important;
        color: #73BCE0;
        border-radius:8px;
        /*border-radius:0px;*/
    }
    .markdown-body pre 
    .htmlembedded,
    .markdown-body pre 
    .diff  {
        color: #C8D4C8 !important;
    }
    .markdown-body pre
    .hljs-tag {
        color: #6D726E;
    }
    .markdown-body pre 
    .token.keyword {
        color: #C586C0;
    }
    .markdown-body pre 
    .token.string {
        color: #C68362;
    }
    .markdown-body pre 
    .hljs-string {
        color: #C68362;
    }
    .markdown-body pre 
    .hljs-comment,
    .markdown-body pre 
    .token.comment {
        color: #6A9955;
    }
    .markdown-body pre 
    .hljs-attr {
        color: #73BCE0;
    }
    .markdown-body pre 
    .hljs-name {
        color:#569CD6;
    }
    .markdown-body pre 
    .token.operator {
        color:#C8D4C8;
        background:transparent;
    }
    .markdown-body pre 
    .token.property {
      color: #73BCE0;
    }
    .markdown-body pre 
    .token.function {
      color: #DCDCAA;
    }
    .markdown-body pre 
    .token.builtin {
      color: #34B294;
    }
    .markdown-body pre 
    .token.number {
      color: #B5CEA8;
    }
    .markdown-body pre 
    .token.constant {
      color: #3BC1FF;
    }
    .markdown-body pre 
    .hljs-addition {
      color: #96D47D;
      background: #373D29;
    }
    .markdown-body pre 
    .hljs-deletion {
      color: #E76A6A;
      background: #4B1818;
    }
</style>