I recently upgraded my project to TypeScript 4.4.3 from 3.9.9.
My project's using "strictNullChecks": true,
in its tsconfig.json
, and runs in the browser, not server-side on Node.
In TypeScript 4.4.3, it seems like the type declarations for top
has changed to WindowProxy | null
(node_modules/typescript/lib/lib.dom.d.ts
)
This means that I get the following error1 wherever I try to access properties of top
2: TS Playground
const topUrl = top.window.location.href; // => Object is possibly 'null'.
How can I ignore this category of errors only for when top
is possibly null?3
1 I understand that this error is warning me against the scenario where my website is loaded in an iframe, and therefore can't access top
due to XSS. This isn't an issue because my 'X-Frame-Options'
is set to 'sameorigin'
and will therefore refuse to load my website in a cross-origin iframe.
2 I access properties of top
because I use iframes inside my project a lot, where it loads sub-pages on the same domain.
3 I could use the following fixes to get around this Object is possibly 'null'.
, but I'd prefer not to, as my project is quite large and this fix would be tedious with minimal improvement.
let topUrl = top?.window.location.href || '';
let topUrl = '';
if (top) {
topUrl = top.window.location.href;
}
I could also ignore these errors on every line with // @ts-ignore
, but there's a lot of references to top
and I don't want to clutter the project (also, other TypeScript errors on the same line would be ignored).
// @ts-ignore
const topUrl = top.window.location.href;
I found a solution which would possibly fit your needs. And there are 2 versions of the solution you can take into consideration.
Both of these versions work by overriding the built-in lib-dom
with a npm package @types/web
which is also provided by Microsoft.
[email protected]
Follow steps below and things are gonna work as you expect without any other code modifications:
TypeScript
4.5.0:
npm i -D [email protected]
or install globally
npm i -g [email protected]
@types/[email protected]
type package which has top: Window
type
npm i -D @typescript/lib-dom@npm:@types/[email protected]
I have made some simple tests on this solution and managed to get behaviour you want.
The only shortcoming of this solution is that [email protected] is still beta currently. But It worth your consideration since its final release will be just on next month.
TypeScript 4.5 Iteration Plan
typescript
4.4.3 and switch the built-in dom lib.install @types/web
npm i -D @types/[email protected]
notice that the install command is different from the above one.
Update your tsconfig.json
. There are two cases to consider depending on if you have lib
defined in your tsconfig.json
or not.
The drawback of this second version of solution is, it cannot prevent your dependencies in node_modules
from pulling in the TypeScript DOM library.
Please bear in mind that despite @types/web
is up to version 0.0.40, only version 0.0.1 of @types/web
has top
typed top: Window
instead of top: WindowProxy | null
which is what you need.
You decided to upgrade your compiler version, and, as mentioned in a comment, major software version changes almost always come with breaking API changes.
The correct way to solve your issue (prevent compiler errors) is to modify your source code to satisfy the compiler. You said that modifying your source code in this way would be a chore, and asked about modifying the compiler configuration instead such that you can avoid modifying your source code.
It is not possible to override the types in lib.dom.d.ts
in new type declarations. TypeScript will emit additional errors if you attempt to do this, even if you disable type-checking of your new declaration file, resulting in an incompatible merging of your new declarations. Your only option is to exclude the built-in DOM library and provide your own modified version of it.
Here is an overview of how to do that:
You haven't provided your tsconfig.json
file, so here's an example to use as a base, with the assumption that your source is organized in your repo's src/
directory:
Note:
"strict": true
implies"strictNullChecks": true
{
"compilerOptions": {
"isolatedModules": true,
"lib": [
"esnext",
"dom",
"dom.iterable"
],
"module": "esnext",
"outDir": "dist",
"strict": true,
"target": "esnext"
},
"include": [
"./src/**/*"
]
}
lib.dom.d.ts
libraryFirst download the lib.dom.d.ts
file from the tag that matches your TypeScript version (4.4.3): https://github.com/microsoft/TypeScript/blob/v4.4.3/lib/lib.dom.d.ts
Move the file to src/types/lib.dom.d.ts
in your project
Remove the triple-slash reference on line 18 by deleting the entire line. (This will allow you to continue using other built-in libraries.)
Modify line 17286 from this:
readonly top: WindowProxy | null;
to this:
readonly top: WindowProxy;
Modify line 18350 from this:
declare var top: WindowProxy | null;
to this:
declare var top: WindowProxy;
Save the file
Now that you have a replacement library for the DOM types in your program, you need to tell the compiler to use it that way. Here's what you need to change:
{
"compilerOptions": {
// ...
"lib": [
"esnext",
"dom", // Delete this from the array
"dom.iterable"
],
// ...
// Add this array property
"typeRoots": [
"./node_modules/@types",
"./src/types"
]
},
// ...
}
So the modified tsconfig.json
now looks like this:
{
"compilerOptions": {
"isolatedModules": true,
"lib": [
"esnext",
"dom.iterable"
],
"module": "esnext",
"outDir": "dist",
"strict": true,
"target": "esnext",
"typeRoots": [
"./node_modules/@types",
"./src/types"
]
},
"include": [
"./src/**/*"
]
}
That's it. Now you should be able to compile your program and reference window.top
or just the global top
as a non-nullable value without a compiler error.
You'll need to repeat this process every time you upgrade TypeScript. Is this strategy more sustainable than modifying your source code? That's up to you.
I preface this answer with a strong warning that I would not do this to my project and encourage anyone in this position to fix the errors the proper way using null coalescing or not null assertion. EG:
window.top!.scrollTo()
top!.scrollTo()
window.top?.scrollTo()
top?.scrollTo()
// etc..
Even though theres 1500 I think using some regular expression you could easily target a large portion of those errors and fix with ease. With that said heres some other options:
I havent done this in a production project and might result in some other strange errors, its largely untested by myself outside of quick testing
The summary of this solution is you could clone the lib.dom.ts file and make the modifications by hand.
./node_modules/typescript/lib/lib.dom.d.ts
to somewhere in your project, say ./lib.dom.modified-4.4.3.d.ts
window.top
and top
types
// old
// readonly top: WindowProxy | null;
// new
readonly top: WindowProxy;
...
// old
// declare var top: WindowProxy | null;
// new
declare var top: WindowProxy;
tsconfig.json
to remove dom
as one of the libraries and add it to the list of types{
"compilerOptions": {
"lib": [
"ES6"
],
"strictNullChecks": true,
"module": "commonjs",
"target": "ES6",
"types": [
"./lib.dom.modified-4.4.3"
]
},
"include": [
"src/**/*"
]
}
Now you have a custom dom library with the top
property not nullable
Alternatively you could make a patch for lib-dom using git and apply it post install. Details about how to do that are outlined in several solutions of this question How to overwrite incorrect TypeScript type definition installed via @types/package
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With