Examples from Youtube video "Exploring Advanced TypeScript Concepts" by Headway

12 Aug 2022

Lookup types

A nested type inside another type

type Route = {
  origin: {...};
  destination: {...};
}

type Origin = Route['origin'];
type Destination = Route['destination'];

Generics

export enum TaskType {
  feature = "feature",
  bug = "bug",
}

type Task<T = TaskType> = {
  name: string;
  type: T;
};

const whatever: Task = { name: "SSO", type: TaskType.feature };

Extract Utility Function

type Trip =
  | { origin: { uuid: string; city: string; state: string } }
  | { originUuid: string };

type TripWithOriginRef = Extract<Trip, { originUuid: string }>;
type TripWithOriginWhole = Extract<Trip, { origin: { uuid: string } }>;

const tripOriginRef = { originUuid: "123" };
const tripOriginWhole = {
  origin: { uuid: "123", city: "Denvor", state: "Colorado" },
};

const isRef = (trip: Trip): trip is TripWithOriginRef => {
  return trip.hasOwnProperty("originUuid");
};

const isDraft = (trip: Trip): trip is TripWithOriginWhole => {
  return trip.hasOwnProperty("origin");
};

const result = [tripOriginRef, tripOriginWhole].filter(isRef);

Conditional Types

type Diesel = {
  type: "petroleum" | "bio" | "synthetic";
};

type Gasoline = {
  type: "hybrid" | "conventional";
};

type Bus = {
  engine: Diesel;
};

type Car = {
  engine: Gasoline;
};

type Engine<T> = T extends { engine: unknown } ? T["engine"] : never;

type BusEngine = Engine<Bus>;

const bugEngine: BusEngine = {
  type: "bio",
};

const carEngine: Engine<Car> = {
  type: "hybrid",
};

type Bicycle = {
  power: "limbs";
};

type NoEngine = Engine<Bicycle>;

// const noEngine: NoEngine = { type: "limbs" };
// Type '{ type: string; }' is not assignable to type 'never'.ts(2322)

enum Priority {
  mustHave,
}

const backlog = {
  releases: [
    {
      name: "Sprint 1",
      epics: [
        {
          name: "Account Management",
          tasks: [
            { name: "SSO", priority: Priority.mustHave },
            { name: "Email Notifications", priority: Priority.mustHave },
          ],
        },
      ],
    },
  ],
};

type Unarray<T> = T extends Array<infer U> ? U : T;

type Release = Unarray<typeof backlog["releases"]>;

Omit

import React from "react";
import { Button, ButtonProps } from "@material-ui/core";

// you don't want people to change the variant of your button
type Props = Omit<ButtonProps, "variant">;

const BrandButton: React.FC<Props> = ({ children, variant, ...rest }) => {
  return <Button {...rest}>{children}</Button>;
};

Pick and Omit in TypeScript have same semantics with those in lodash, they work on types instead of objects.