Skip to content

TypeScript

Useful Helper Types

ts
/*
  Must include at least one attribute. The source object should use optional fields.

  How to use:
  type MyObject = {
    name?: string
    firstName?: string
  }
  type Another = AtLeastOne<MyObject, "name" | "firstName">
*/
type AtLeastOne<T, K extends keyof T> = {
  [key in K]: Required<Pick<T, key>>
}[K];

/*
  Allow exactly one key from a required object type.

  How to use:
  type MyObject = {
    name: string
    firstName: string
  }
  type Another = OnlyOne<MyObject>
*/
type OnlyOne<T> = {
  [K in keyof T]:
    Pick<T, K> &
    Partial<Record<Exclude<keyof T, K>, never>>
}[keyof T];

// Array must not be empty.
type NonEmptyArray<T> = [T, ...T[]];

// Extract a type from another type.
type NewType = Extract<ReferredType, { someAttribute: unknown }>["someAttribute"];

// Declare a component type in Vue.
type ActionButtonType = InstanceType<typeof ActionButton> | undefined;

Useful tsconfig Settings

json
{
  "compilerOptions": {
    "baseUrl": ".", // Set the base URL to the project root.
    "paths": {
      "~/*": ["src/*"] // Alias "~/commons/utils" to "src/commons/utils".
    },
    "moduleResolution": "node", // Use Node.js module resolution.
    "esModuleInterop": true // Enable ES module interop for default imports.
  }
}

Minimal Strict Config

tsconfig.json

json
{
  "compilerOptions": {
    /* Language and Environment */
    "target": "ESNext",

    /* Modules */
    "module": "ES2022",
    "rootDir": "./src",
    "moduleResolution": "node",
    "baseUrl": "./src",
    "paths": {
      "*": ["./*"]
    },

    /* Emit */
    "outDir": "./dist",

    /* Interop Constraints */
    "esModuleInterop": true,
    "forceConsistentCasingInFileNames": true,

    /* Type Checking */
    "strict": true,
    "skipLibCheck": true
  }
}

Vue and Neovim

NOTE

The TypeScript plugin workaround below was useful in older Neovim and Volar setups. Keep it only if your current editor still needs it.

sh
npm install -D typescript @vue/typescript-plugin
json
{
  "compilerOptions": {
    "plugins": [
      {
        "name": "@vue/typescript-plugin"
      }
    ]
  }
}

TypeScript and JavaScript Tips

globalThis

  • window only exists in browsers.
  • global only exists in Node.js.
  • globalThis works across browsers, Node.js, workers, and edge runtimes.

typeof

Use typeof when a global may not exist at all.

js
console.log(typeof globalThis.crypto !== "undefined");
// true or false, with no ReferenceError

console.log(globalThis.someNonExistentProperty !== undefined);
// false, because the property lookup is allowed

console.log(someNonExistentProperty !== undefined);
// ReferenceError: someNonExistentProperty is not defined

Using typeof globalThis.crypto !== "undefined" prevents this error because typeof never throws an error even if the property is missing.