typescript: how to extend the window with declaration merging

2021-05-20

 | 

~2 min read

 | 

347 words

There are a number of ways to instrument a Redux implementation to enable the Redux Dev Tools. The majority of the options require passing some form of a parameter into the createStore invocation.

const store = createStore(
  reducer,
  window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__(),
)

The problem is that if you’re using Typescript, the Window object doesn’t have __REDUX_DEVTOOLS_EXTENSION__ as a property.

This was the set up for learning about how to extend all sorts of interfaces, namespaces, etc. with Typescript.

What can we do? There are a few solutions, and Marius Schultz has a great write up. Some highlights are:

  1. Type Casting
  2. Interface Declaration Merging

Type Casting

This is the most pragmatic approach and simplest to implement. If we want to extend the window object, we could do something like the following:

;(window as any).__MY_NEW_INTERFACE__

We don’t have to cast it as any, but it certainly makes it simplest. We could also do something like:

type ExtendedWindow = Window &
  typeof globalThis & {
    __REDUX_DEVTOOLS_EXTENSION__: any
  }
;(window as ExpandedWindow).__MY_NEW_INTERFACE__

The problem is that we’d need to do this every time we want to use this expanded definition of window.

Interface Declaration Merging

A more expansive approach is interface declaration merging which at the “most basic level […] mechanically joins the members of both declarations into a single interface with the same name.”

While this is typically used to merge two interfaces of the same name, it can also be used to add declarations from inside a module. This is exactly what we’re going to do.

declare global {
  interface Window {
    __MY_NEW_INTERFACE__: any
  }
}

window.__MY_NEW_INTERFACE__ // no error

Wrap Up

As with many problems in engineering, there’s more than one way to skin the cat - all with their own advantages and disadvantages. The first step, before evaluating which approach makes sense, is to understand the problem space. Hopefully this helps you in the future. I know I’ll be referring back to it when I inevitably forget how to extend an interface like Window!


Related Posts
  • Typescript: Interfaces vs. Types


  • Hi there and thanks for reading! My name's Stephen. I live in Chicago with my wife, Kate, and dog, Finn. Want more? See about and get in touch!