Skip to content

Latest commit

 

History

History
129 lines (97 loc) · 5.16 KB

type-safe-monkey.md

File metadata and controls

129 lines (97 loc) · 5.16 KB

Item 47: Prefer Type-Safe Approaches to Monkey Patching

Things to Remember

  • Prefer structured code to storing data in globals or on the DOM.
  • If you must store data on built-in types, use one of the type-safe approaches (augmentation or asserting a custom interface).
  • Understand the scoping issues of augmentations. Include undefined if that's a possibility at runtime.

Code Samples

document.monkey = 'Tamarin';
//       ~~~~~~ Property 'monkey' does not exist on type 'Document'

💻 playground


(document as any).monkey = 'Tamarin';  // OK

💻 playground


(document as any).monky = 'Tamarin';  // Also OK, misspelled
(document as any).monkey = /Tamarin/;  // Also OK, wrong type

💻 playground


interface User {
  name: string;
}

document.addEventListener("DOMContentLoaded", async () => {
  const response = await fetch('/api/users/current-user');
  const user = (await response.json()) as User;
  window.user = user;
  //     ~~~~ Property 'user' does not exist
  //          on type 'Window & typeof globalThis'.
});

// ... elsewhere ...
export function greetUser() {
  alert(`Hello ${window.user.name}!`);
  //                    ~~~~ Property 'user' does not exist on type Window...
}

💻 playground


declare global {
  interface Window {
    /** The currently logged-in user */
    user: User;
  }
}

💻 playground


document.addEventListener("DOMContentLoaded", async () => {
  const response = await fetch('/api/users/current-user');
  const user = (await response.json()) as User;
  window.user = user;  // OK
});

// ... elsewhere ...
export function greetUser() {
  alert(`Hello ${window.user.name}!`);  // OK
}

💻 playground


declare global {
  interface Window {
    /** The currently logged-in user */
    user: User | undefined;
  }
}

// ...
export function greetUser() {
  alert(`Hello ${window.user.name}!`);
  //             ~~~~~~~~~~~ 'window.user' is possibly 'undefined'.
}

💻 playground


type MyWindow = (typeof window) & {
  /** The currently logged-in user */
  user: User | undefined;
}

document.addEventListener("DOMContentLoaded", async () => {
  const response = await fetch('/api/users/current-user');
  const user = (await response.json()) as User;
  (window as MyWindow).user = user;  // OK
});

// ...
export function greetUser() {
  alert(`Hello ${(window as MyWindow).user.name}!`);
  //             ~~~~~~~~~~~~~~~~~~~~~~~~~ Object is possibly 'undefined'.
}

💻 playground