Published on

How to Explicitly Set a New Property on `window` in TypeScript

Authors
  • Name
    Ripal & Zalak
    Twitter

When working with web applications, there are times when you may need to add custom properties to the global window object. While this is straightforward in plain JavaScript, TypeScript’s strict typing system introduces additional challenges. This article explains how to explicitly set a new property on window in TypeScript while maintaining type safety and avoiding common pitfalls.

Why TypeScript Complains

In TypeScript, the window object is typed as the Window interface, which contains predefined properties and methods. When you attempt to add a custom property (e.g., window.myProperty), TypeScript raises an error because the property is not defined on the Window interface.

declare global {
  interface Window {
    myProperty: string
  }
}

window.myProperty = 'Hello, world!' // ✅ No errors

Common Solutions

1. Extend the Window Interface

The most type-safe way to add properties to window is by extending the Window interface using declaration merging. Here’s how:

// Create a global declaration
declare global {
  interface Window {
    myCustomProperty: string
  }
}

// Assign a value to the custom property
window.myCustomProperty = 'This is a custom property'

console.log(window.myCustomProperty) // Output: This is a custom property

This approach ensures:

  • Type Safety: TypeScript knows about the new property, enabling autocompletion and error checking.
  • Global Scope: The property is available wherever window is used in the project.

2. Using Type Assertions

If you want a quick and less strict solution, you can use type assertions to temporarily bypass TypeScript’s type checks:

;(window as any).myCustomProperty = 'This is a quick hack'

console.log((window as any).myCustomProperty)

While this works, it has some drawbacks:

  • Lack of Type Safety: No autocompletion or error checking for the custom property.
  • Risk of Bugs: Potential runtime errors if the property is accessed incorrectly.

This method should be used sparingly and is best suited for prototyping or temporary fixes.

3. Create a Custom Interface

For better modularity, you can define a custom interface and cast window to that interface:

interface MyWindow extends Window {
  myNamespace: {
    someFunction: () => void
  }
}

declare let window: MyWindow

window.myNamespace = {
  someFunction: () => {
    console.log('Hello from myNamespace')
  },
}

window.myNamespace.someFunction() // Output: Hello from myNamespace

This approach is particularly useful when dealing with complex objects or namespaces.

4. Use window['propertyName']

If you prefer not to modify TypeScript’s type definitions, you can use the index signature to add properties:

window['myDynamicProperty'] = 'Dynamic value'
console.log(window['myDynamicProperty']) // Output: Dynamic value

While this avoids TypeScript errors, it’s less elegant and doesn’t provide type safety.

Best Practices

  • Avoid Overuse of Globals: Minimize the use of global properties to reduce the risk of conflicts and maintain modularity.
  • Prefer Type-Safe Solutions: Extending the Window interface is the recommended approach for production code.
  • Document Custom Properties: Clearly document custom properties for team members and future maintainers.

Example: Redux DevTools Integration

A common use case for extending the window object is enabling Redux DevTools in a TypeScript project:

declare global {
  interface Window {
    __REDUX_DEVTOOLS_EXTENSION_COMPOSE__?: typeof compose
  }
}

const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose

This ensures TypeScript recognizes the Redux DevTools property while maintaining type safety.

Conclusion

Adding custom properties to the window object in TypeScript requires careful consideration of type safety and maintainability. By extending the Window interface or using other type-safe methods, you can enhance your project while leveraging TypeScript’s powerful features. Choose the approach that best fits your needs and coding standards.