TypeScript: Unable to extend window in TS 3.6
TypeScript Version: 3.6.2
Search Terms:
- globalThis
Code
This code passed the type checker in TS 3.5:
interface MyWindow extends Window {
foo: string;
}
(window as MyWindow).foo = 'bar';
but fails with this error in TS 3.6:
Conversion of type 'Window & typeof globalThis' to type 'MyWindow' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first.
Property 'foo' is missing in type 'Window & typeof globalThis' but required in type 'MyWindow'.ts(2352)
Following the release notes, I can work around the issue by changing from interface to type and using an intersection:
type MyWindow = (typeof window) & {
foo: string;
}
(window as MyWindow).foo = 'bar';
but this feels more obscure than the old way. Is this WAI? What’s the rationale for the non-extendable Window?
Expected behavior:
An interface should be able to extend Window.
Actual behavior:
The error above.
Playground Link: 3.6 isn’t on the playground yet.
Related Issues:
About this issue
- Original URL
- State: closed
- Created 5 years ago
- Reactions: 28
- Comments: 18 (10 by maintainers)
Commits related to this issue
- tainted ref: don't use TS 3.6 just yet Typescript 3.6 causes an incompatibility in eslint (or at least the risk of it) which makes for quite verbose warnings. Cf. Ie0bfd9871384c4daf9a3d4a8e5801edcc0a... — committed to wikimedia/mediawiki-extensions-Wikibase by wiese 5 years ago
- Update git submodules * Update Wikibase from branch 'master' to 0f1c1752140e77f6c90fcc21271178a5d9bf9358 - Merge "tainted ref: don't use TS 3.6 just yet" - tainted ref: don't use TS 3.6 just ye... — committed to wikimedia/mediawiki-extensions by deleted user 5 years ago
- bridge: update to TypeScript 3.7 Apply latest TypeScript version[0] and fix resulting complaints. The question of how to extend window may be noteworthy[1] as the `declare global` in MwWindow does so... — committed to wikimedia/mediawiki-extensions-Wikibase by wiese 4 years ago
- Update git submodules * Update Wikibase from branch 'master' to a5e6df0da5fa9b4cd9af63cb187a8477dc289775 - Merge "bridge: update to TypeScript 3.7" - bridge: update to TypeScript 3.7 A... — committed to wikimedia/mediawiki-extensions by deleted user 4 years ago
Augmentation of
windowis usually done by using interface merging:or
For non-module scenarios.
quick and dirty 💥
(window as any).foo = "bar";@fatcerberus I don’t believe this statement is correct:
I put this in a new file in my project (
foo.ts) that’s not imported anywhere:Then, in another file, I can write:
without importing
foo. There’s no error and I see the documentation. So I do not believe the augmentation is scoped to the module at all. It is as global as thedeclare globalimplies.@orta Can I ask why it needs to be registered as a module?
For me this doesn’t work until the add the
export {}- I’m just trying to understand why.Why not include
globalThisat the point of the assertion?This is indeed working as intended.
window.foo = 'bar''s runtime behaviour is best represented by an interface merge, not a subtype. Can you actually even extend window? It seems like a convenient lie to the compiler.As for the weird error, Window has a numeric index signature
{ [n: number]: Window }so thatwindow[0] : Window. But when making sure anextendsis legal, the compiler checks that numeric properties from the other side of the intersection have typeWindow. Normally, that’s properties with names like'0'and'1', but guess whatNaNandInfinityare also numbers, so we check that their types are compatible with Window’s index signatures. SinceNaN: numberandnumberis not assignable toWindow, the compiler reports that as an error.@fatcerberus the property is named
NaN, which is definitely a number. The funniest number.To force the file to be registered as a module, not a script
The solution we recommend at Google is to make a local subtype for explicit casts, which avoids polluting the global type definitions. Because of the
NaN/Infinityissue, we have a library withwhich can then be used like so: (playground)
If you want the type augmentation to be global and ambient, this works fine:
The trick is, you must put this in a file with the extension
.d.ts(and there must not be a same-named file with just the extension.tsin the same directory. Then this will function as an ambient type and declaration merging will kick in. No import required.