TypeScript

TypeScript #

Properly handle unknown errors #

interface ErrorWithCode extends Error {
    code: string;
}

function isErrorWithCode(err: unknown): err is ErrorWithCode {
    if (err instanceof Error) {
        if ('code' in err)) {
            return true;
        }
    }

    return false;
}

function doSomething(key: string) {
    try {
     ...
    } catch (ex) {
        if (isErrorWithCode(ex) && ex.code === 'ENOENT') {
          // do something
        } else {
            throw ex;
        }
    }

Get properties from unknown objects #

function getName(something: unknown) {
  let name: string | undefined;

  if (
    typeof something === "object" &&
    something !== null &&
    "name" in something
  ) {
    if (typeof something.name === "string") {
      name = something.name;
    }
  }

  return name;
}

Switch expression #

switch (expression) {
  case constant - expression1: {
    //statements;
    break;
  }
  case constant_expression2: {
    //statements;
    break;
  }
  default: {
    //statements;
    break;
  }
}

Delete generated js files #

tsc --build --clean

TypeScript ? question mark - optional chaining #

const test = undefined;
console.log(test.length);
// TypeError: Cannot read properties of undefined (reading 'length')
const test = undefined;
console.log(test?.length)
// undefined

TypeScript project references , nested tsconfig.json #

  • Nested tsconfigs do NOT automatically assume different projects
  • By adding references in the root project, you instruct tsc to build first the referenced projects and then the root one
  • The root tsconfig should exclude the directories of the referenced projects and the child projects should stop the exclusion on their own tsconfig
  • Referenced projects should have ‘composite’: true and noEmit: false in their tsconfigs
  • Project references

TypeScript - improve performance #

tsconfig.json types array not getting used #

Delete the tsconfig.tsbuildinfo cache file!

Use of never for exhaustiveCheck #

function doSwitch(value: MyEnum): boolean {
  switch (value) {
    case MyEnum.A:
      return true;
    case MyEnum.B:
      return true;
    default: {
      // Use never type here
      const exhaustiveCheck: never = value;
      throw new Error(exhaustiveCheck);
    }
  }
}

Assign a property on constructor #

export class Lock {
  constructor(private lockName: string) {}
}

Assert not null with typescript typeguard #

function expectNotNull<T>(value: T): asserts value is NonNullable<T> {
  expect(value).not.toBeNull();
}

Assert not undefined with typescript typeguard #

function expectNotUndefined<T>(value: T): asserts value is NonUndefined<T> {
  if (value === undefined) {
    throw new Error("Value is undefined");
  }
}

type NonUndefined<T> = T extends undefined ? never : T;

ESM modules error: CustomError: Cannot find module … imported from … #

Add the “.js” NOT “.ts” extension in the import, i.e. import { EfficientQueue } from './EfficientQueue.js';

String comparator for sorting #

String.localCompare()

const items = ["réservé", "premier", "communiqué", "café", "adieu", "éclair"];
items.sort((a, b) => a.localeCompare(b));

Convert string array to union type #

const myVals = ["A", "B"] as const;

type myUnionType = (typeof myVals)[number];

Convert a property of an object array to union type #

const colors = [
  { no: 1, name: "Red" },
  { no: 2, name: "Green" },
  { no: 3, name: "Blue" },
] as const;

type ColorName = (typeof colors)[number]["name"]; // 'Red' | 'Green' | 'Blue'

Convert string array to object keys #

const myVals = ["a", "b"] as const;

type myObj = {
  [P in (typeof myVals)[number]]: boolean;
};

Convert object keys to union type #

const yesNo = {
  yes: "Yes",
  no: "No",
} as const;

type YesNo = (typeof yesNo)[keyof typeof yesNo];

Convert object keys to array #

const yesNo = {
    yes: 'Yes',
    no: 'No
} as const;

const getKeys = Object.keys as <T extends object>(obj: T) => Array<keyof T>

const yesNoArr = getKeys(yesNo);

TypeScript Object vs {} vs object #

TypeScript has three confusing types: Object, {} and object.

You can assign null and undefined to all three types if strictNullChecks compiler option is disabled otherwise the compile error occurs.

  • Object: Contains stuff (like toString(), hasOwnProperty()) that is present in all JavaScript objects. Any value (primitive, non-primitive) can be assigned to Object type.
  • {}: {} is an empty object. It is pretty much the same as Object in runtime but different in compile time. In compile time {} doesn’t have Object’s members and Object has more strict behavior (see the @golmschenk’s comment).
  • object: object was introduced in TypeScript 2.2. It is any non-primitive type. You can’t assign to it any primitive type like bool, number, string, symbol.

Differentiating between types in a union based on a key #

Use the ‘in’ operator as a type guard

interface Car {
  color: string;
  doors: number;
  speed: number;
  bootSize: number;
}

interface Bike {
  color: string;
  speed: number;
  type: string;
}

function doSomething(vehicle: Car | Bike) {
  if ("doors" in vehicle) {
    console.log(vehicle.bootSize);
  } else {
    console.log(vehicle.type);
  }
}

Destructuring #

Destructuring to new variables #

let { r, g, b } = colors;

Destructuring to new variables with different names #

let { r: red, g: green, b: blue } = colors;

Destructuring to existing variables #

let r, g, b;
({ r, g, b } = colors);

Destructuring to existing variables with different names #

let red, green, blue;
({ r: red, g: green, b: blue } = colors);

Destructuring into another object with same property names #

let myColor = { r: 0, g: 0, b: 0 };..
({ r: myColor.r, g: myColor.g, b: myColor.b } = colors);

Destructuring into another object with different property names #

let myColor = { red: 0, green: 0, blue: 0 };.
({ r: myColor.red, g: myColor.green, b: myColor.blue } = colors);

Array destructuring #

const RGB = [255, 180, 151];
const [red, green, blue] = RGB;

Destructure, Skip the first item and set default value #

const RGB = [255, 180];
// Set a default value (51) for the third item (`blue`)
const [, green, blue = 51] = RGB;

Access nested properties #

const student = {
  name: "John Doe",
  age: 16,
  scores: {
    maths: 74,
    english: 63,
  },
};

const {
  name,
  scores: { maths, science = 50 },
} = student;

Swap variables #

let width = 300;
let height = 400;

[width, height] = [height, width];

Nested array destructuring #

const color = ["#FF00FF", [255, 0, 255], "rgb(255, 0, 255)"];
const [hex, [red, green, blue]] = color;

Rest items #

const rainbow = [
  "red",
  "orange",
  "yellow",
  "green",
  "blue",
  "indigo",
  "violet",
];
const [red, , yellow, ...otherColors] = rainbow;
console.log(otherColors); // ['green', 'blue', 'indigo', 'violet']

Running esm scripts using ts-node #

In tsconfig.json

"ts-node": {
    "moduleTypes": {
        "./my-script.ts": "esm"
    }
}

To execute

node --loader ts-node/esm ./my-script.ts

Strict TypeScript Config #

Utility types libraries #

Other resources #

TypeScript Training #