Item 47: Prefer Type-Safe Approaches to Monkey Patching
May 10, 2024 ยท View on GitHub
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
undefinedif that's a possibility at runtime.
Code Samples
document.monkey = 'Tamarin';
// ~~~~~~ Property 'monkey' does not exist on type 'Document'
(document as any).monkey = 'Tamarin'; // OK
(document as any).monky = 'Tamarin'; // Also OK, misspelled
(document as any).monkey = /Tamarin/; // Also OK, wrong type
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...
}
declare global {
interface Window {
/** The currently logged-in user */
user: User;
}
}
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
}
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'.
}
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'.
}