How to prevent "Property '...' does not exist on type 'Global'" with jsdom and typescript?
TypescriptJsdomtypescript2.0Typescript Problem Overview
I try to convert an existing project to use Typescript and I have problems doing so with my testing setup.
I had a setup file for my tests that sets up jsdom so that all my DOM interacting code works during my tests. Using Typescript (ts-node with mocha) I always get errors like this:
Property 'window' does not exist on type 'Global'.
To prevent this I tried patching the NodeJS.Global interface like this:
declare namespace NodeJS{
interface Global {
document: Document;
window: Window;
navigator: Navigator;
}
}
But this didn't change anything.
How do I enable those browser properties on the NodeJS global variable?
Extras:
This is my mocha setup.ts
:
import { jsdom, changeURL } from 'jsdom';
const exposedProperties = ['window', 'navigator', 'document'];
global.document = jsdom('');
global.window = global.document.defaultView;
Object.keys(global.document.defaultView).forEach((property) => {
if (typeof global[property] === 'undefined') {
exposedProperties.push(property);
global[property] = global.document.defaultView[property];
}
});
global.navigator = {
userAgent: 'node.js',
};
changeURL(global.window, 'http://example.com/');
Typescript Solutions
Solution 1 - Typescript
Original Answer To Avoid Error
Put this at the top of your typescript file
const globalAny:any = global;
Then use globalAny instead.
globalAny.document = jsdom('');
globalAny.window = global.document.defaultView;
Updated Answer To Maintain Type Safety
If you want to keep your type safety, you can augment the existing NodeJS.Global
type definition.
You need to put your definition inside the global scope declare global {...}
Keep in mind that the typescript global
scope is not the same as the NodeJS interface Global
, or the node global property
called global
of type Global
...
declare global {
namespace NodeJS {
interface Global {
document: Document;
window: Window;
navigator: Navigator;
}
}
}
Solution 2 - Typescript
In addition to other answers, you can also simply cast global
directly at the assignment site:
(global as any).myvar = myvar;
Solution 3 - Typescript
I fixed this problem by doing this...
export interface Global {
document: Document;
window: Window;
}
declare var global: Global;
Solution 4 - Typescript
This is the right solution, not using Typescript's namespaces. It is also compatible with all eslint default rules:
// Declare a type.
interface CustomNodeJsGlobal extends NodeJS.Global {
myExtraGlobalVariable: number;
// You can declare anything you need.
}
Use it:
// Tell Typescript to use this type on the globally scoped `global` variable.
declare const global: CustomNodeJsGlobal;
function doSomething() {
// Use it freely
global.myExtraGlobalVariable = 5;
}
doSomething();
Solution 5 - Typescript
Avoid typecasting any
, it removes the purpose of typings. Instead install the type definitions needed (e.g. yarn add --dev @types/jsdom @types/node
) and import to use:
import { DOMWindow, JSDOM } from 'jsdom'
interface Global extends NodeJS.Global {
window: DOMWindow,
document: Document,
navigator: {
userAgent: string
}
}
const globalNode: Global = {
window: window,
document: window.document,
navigator: {
userAgent: 'node.js',
},
...global
}
Solution 6 - Typescript
declare namespace NodeJS {
export interface Global { window: any;
}
}
Solution 7 - Typescript
A simple method can be use extend the Typescript "Window" type
step 1: append Window object
interface Window {
foo:string // any type of your foo property
}
step 2: declare
let foo = 'value of foo'
step 3: add to window object
window.foo
or
window.foo = foo
work for me...