Accessing Window Object from .tsx file - javascript

Typically in my .ts files I can access the window object by calling something such as:
(<any>window).myObject
I am getting compilation errors for this in my .tsx files. Is there any way I can access it from a .tsx file?
Thanks.

You can use the as syntax for type assertion. This is the alternate syntax for type assertion as <type>obj conflicts with JSX syntax:
(window as any).myObject
The above will work, however if you want strong typing consider augmenting the Window interface to add your property so you will get compile-time type checking:
declare global {
interface Window {
myObject: YourObjectType;
}
}

The syntax <type> is being deprecated by the ts team. This is because there is too much ambiguity between it and the new jsx syntax. Instead the ts team introduced the as operator for type assertions.
So this syntax:
(window as any).things
is more update to date.
This change was done, because basically, its very tough to tell a compiler when is such a syntax relating to a type or a jsx-element. Likewise the notation becomes much harder to read (see below for example):
<Component>
{<String>something}
</Component>
Some more detail can be found here https://basarat.gitbooks.io/typescript/docs/types/type-assertion.html#as-foo-vs-foo and here https://github.com/Microsoft/TypeScript/issues/296

Further Explanation on #Saravana Answer,
best way is to have this defined in your types folder, and add a definition file with .d.ts extention, i.e: window.d.ts and place your extended definitions for the window object, as typescript global augmentation typically offers merging interfaces.
declare global {
interface Window {
myObject: YourObjectType;
}
}
// but make sure to export that as default so Typescript will consider it automatically on the project
export default global;

Related

TypeScript Augmenting Window not working in separate files

I'm looking to add another property to window. I can do that with this:
// global.d.ts
import { IConfig } from './src/models';
export {};
declare global {
interface Window {
_env: IConfig;
}
}
But then when I try to reference this new property in a different file, it complains:
// src/util.ts
// Property '_env' does not exist on type 'Window & typeof globalThis'.
export const URL = `https://example.com/${window._env.path}`;
But when I combine these into the same file, everything is fine and there are no errors. Is there anyway I can have these in a separate file?
I'm using TypeScript 4.1.2.
I went down a bit of a rabbit hole but found this relevant documentation
I was able to accomplish this exactly as you have written (different type of course) in an existing angular 8 project and svelte project using their existing polyfills.ts files for my global declaration.
Are you sure you tsconfig.json is compiling everything correctly?

Override typescript module exports definition

I have installed plugin that ship with types definition.
declare module 'autobind-decorator' {
const autobind: ClassDecorator & MethodDecorator;
export default autobind;
}
But, I thought that type definition was wrong. I need to change to this
declare module 'autobind-decorator' {
const autobind: ClassDecorator & MethodDecorator;
export = autobind;
}
How can I do that?
How can I do that
Fork the project and publish (till the original gets fixed).
More
This is essentially if an author publishes a bad JS lib how do I fix it. You would fix it by forking. TypeScript doesn't offer much magic here.
Reason
If TypeScript offered a way to override it, it would lead to only confusion which definition is used.

ES7 type hinting failure in PhpStorm

I am using TypeScript in my project. JavaScript's array includes() function has been valid since ECMA6. However, when I set my lib parameter in tsconfig to "es6" the following code throws a non-fatal error in the console browser.
['alpha', 'beta', 'gamma'].includes('alpha');
The non-fatal error:
[default] /foo/bar.component.ts:157:28
Property 'includes' does not exist on type 'string[]'.
There is a simple solution. Changing the lib parameter in tsconfig to es7 silences the error in the console. All is well in the code.
However, PhpStorm 2016.3.2 does not recognize the solution. I continue to get a type hint error with the following message:
TS2339: Property 'includes' does not exist on type 'string[]'.
How can I get PhpStorm to recognize that the use of includes() is valid?
I'm not sure what's causing PhpStorm to act that way, but there's another solution which doesn't require you to use the es7 lib.
You can polyfill this definition:
interface Array<T> {
includes(item: T, fromIndex?: number): boolean;
}
And if your environment support this method then you're good to go.
If you are using a module system then you need to do this:
declare "global" {
interface Array<T> {
includes(item: T, fromIndex?: number): boolean;
}
}
Edit
To use the global agumation you put the declare "global" { ... } in a file, let's go with your name for it: polyfills.ts but you need to have a dummy export so that the compiler will compile it as a module, so:
export {};
declare "global" {
...
}
Then wherever you need it you just import like so:
import "./polyfills";
And it should work.
There are two fixes to the PHPStorm issue.
Add a new library to PHPStorm's frameworks. The simplest solution is to go to PHPStorm > Preferences > Languages & Frameworks > JavaScript > Libraries and add the core-js-DefinitelyTyped library.
If that does not work you can modify Nitzan Tomer's solution. If you keep the tsconfig lib value as es7 and add his code to polyfills.ts both the type hinting errors in PHPStorm will be resolved.

TypeScript - Attach module/namespaces to window

I've built a lot of APIs and applications using ES2015, but I am not used to the best practices in TypeScript yet, so maybe you can help me.
Let's say I am building an API/SDK for a shop. The goal is that the user includes my js file and accesses the shop and its namespaces via the window object, much like it is possible with angular and other libs as well.
window.shop.init();
window.shop.cart.get();
window.shop.cart.set();
window.shop.cart.clear();
In ECMAScript 2015, I would write my methods like get and set, import them in my main file and extend the shop object and finally the global object.
// in my cart.js namespace file
export {get} from './get';
// in my shop.js
import * as cart from './cart';
global.shop = {
cart
}
Being a good approach for namespacing in ES2015, it feels kinda wrong in TypeScript having all those module and namespace keywords.
I basically want to achieve the same in TS. I tried things like the following, but with no success.
module shop {
export const cart = {...}
}
(<any>window).shop = shop;
or
namespace shop {
// ...
}
(<any>window).shop = shop;
There where some tutorials claiming that a module is automatically attached to the global/window object, but that did not happen for me.
I am using TypeScript 1.8.10. Any help is greatly appreciated!
There where some tutorials claiming that a module is automatically attached to the global/window object, but that did not happen for me.
Maybe because the code of your namespace is in an (ES6) module instead of in a script? The differences between scripts and modules are detailed here.
The code below makes a global variable shop in the browser if it is loaded as a script, ie with a tag <script src="shop.js"> (or you can concatenate this file with other JavaScript files, for example with uglifyjs).
// File shop.ts
namespace shop {
export const cart = { /* ... */ }
}
If your code is loaded as an ES6 module (ie with the help of Webpack, SystemJS, RequireJS or other), your solution is valid:
(<any>window).shop = shop;
The answer by #paleo is not perfect.
It just suppress the type inference for shop.
I also encounter the similar problem this morning. I tried so many "solutions" on SO, but none of them produce no type error absolutely and enable triggering type jumping in IDE(webstorm or vscode).
Finally, from here
https://github.com/Microsoft/TypeScript/issues/3180#issuecomment-102523512
, I find a reasonable solution to attach typings for global variable which acts as interface/class and namespace both.
Example is below:
// typings.d.ts
declare interface Window {
shop?: MyNamespace & typeof MyNamespace
}
declare interface MyNamespace {
somemethod?()
}
declare namespace MyNamespace {
// ...
}
Now, the code above merges the typings of namespace MyNamespace and interface MyNamespace into the global variable shop(the property of window).

Are ES6 module imports hoisted?

I know that in the new ES6 module syntax, the JavaScript engine will not have to evaluate the code to know about all the imports/exports, it will only parse it and “know” what to load.
This sounds like hoisting. Are the ES6 modules hoisted? And if so, will they all be loaded before running the code?
Is this code possible?
import myFunc1 from 'externalModule1';
myFunc2();
if (Math.random()>0.5) {
import myFunc2 from 'externalModule2';
}
After doing some more research, I've found:
Imports ARE hoisted! according to the spec of ModuleDeclarationInstantiation
ALL the dependent Modules will be loaded before running any code.
This code will have no errors, and will work:
localFunc();
import {myFunc1} from 'mymodule';
function localFunc() { // localFunc is hoisted
myFunc1();
}
It will be a SyntaxError. According to this part of specification:
Module :
ModuleBody
ModuleBody :
ModuleItemList
ModuleItemList :
ModuleItem
ModuleItemList ModuleItem
ModuleItem :
ImportDeclaration
ExportDeclaration
StatementListItem
It means that module can contain only ImportDeclaration's, ExportDeclaration's or StatementListItem's.
According to this StatementListItem could
not contain ImportDeclaration nor ExportDeclaration.
import myFunc1 from 'externalModule1';
is an import declaration, while:
if (Math.random()>0.5) {
import myFunc2 from 'externalModule2';
}
is a statement. So your code will result to a syntax error.
What about "will they all be loaded before running the code?". This part of specification contain next sentence:
NOTE: Before instantiating a module, all of the modules it requested must be available.
So, yeah. They will all be loaded before running the code.
ES6 specification is a subject to change but this draft is explicit:
The static variable resolution and linking pass checks for conflicts
in imported variable names. If there is a conflict between two
imported names, or an imported name and another local binding, then it
is a compile-time error.
And trying to import at runtime is doubtful idea, not only in ES6. Also from the draft:
Compilation resolves and validates all variable definitions and
references. Linking also happens at compile-time; linking resolves and
validates all module imports and exports.
You can see that Babel's ES6 implementation isn't too happy with it.

Categories

Resources