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 JavaScript Arrays Object(ively)
- ES6 Destructuring: The Complete Guide
- Destructuring assignment
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
Utility types libraries #
- piotrwitek/utility-types
- ts-toolbelt TypeScript’s largest utility library
- type-fest - A collection of essential TypeScript types
Other resources #
- tsconfig.json cheat sheet
- ts-reset - improve TypeScript built-in typings
- Instantly generate TypeScript interfaces from JSON
- TypeScript challenges
- TypeScript style guide