Let's say I have a project with two typescript files:
My tsconfig.json:
{
"target": "ES5", // old browser support is required
"lib": ["dom", "es5"], //
"types": [] //
}
I left types setting empty as I don't want to pickup typings from any third party packages (e.g. @types/node typings installed as a dependency of webpack-dev-server package; details are HERE)
Now I decided to use some new JS feature and added the following line to a.ts file:
let clone = Object.assign({}, {a:1});
Luckily, compiler is smart enough to warn me with the following warning:
a.ts:1:20 - error TS2339: Property 'assign' does not exist on type 'ObjectConstructor'.
So I have to add a polyfill (e.g. CORE-JS package) and add an import statement to a.ts:
import 'core-js/modules/es.object.assign'
let clone = Object.assign({}, {a:1});
But typescript compiler is still not happy, as it doesn't have typings for my newly polyfilled assign method . I tried to install @types/core-js package and add its name to my tsconfig.json file, but it didn't work so I created my own type definition file object.d.ts:
interface ObjectConstructor {
assign(target: any, ...sources: any[]): any;
}
Everything started to work. Hooray!
Then, I added another new feature, but this time in b.ts file.
import 'core-js/modules/es.array.from'
console.log(Array.from('foo'));
And I also added a type definition for a new method (file array.d.ts):
interface ArrayConstructor {
from<T>(arrayLike: ArrayLike<T>): T[];
}
And now, as both my typing files are globally accessible, I can use Array.from in a.ts, and Object.assign in b.ts and that's a problem. Unless I add
import 'core-js/modules/es.object.assign'
to b.ts, the code won't work in old browsers and compiler won't tell me about it.
So, here are my questions:
PS: I will not bundle my two files, as they have to be delivered separately. Extracting polyfill imports into a separate modules and later add imports to that module in each file is not a desired option for me, as it will potentially create references to polyfills which are not used in my files and I want to keep result files as small as possible.
This particular form of import statement which you are using
import 'core-js/modules/es.object.assign'
is called "import for side-effects only", for a reason. This statement does not have any effect on module scope where the import appears, the effect, if present, is global and is not obvious unless you examine what the code in the imported module actually does.
These particular polyfills add static methods to global Object
and Array
constructors.
So your type definitions for those polyfills seem to work properly, at least if a.js
and b.js
are going to be run in the same execution context and share the same global objects.
Then, it does not matter in which module you import the polyfill, it will modify shared global objects which are available in all modules, with one exception - when you have statements in the module scope (not inside any function) which are executed immediately when the module is loaded. For those statements, the availablility of polyfills depends on module loading order, but anything that depends on module loading order should be avoided if at all possible.
Is it possible to make my own type definitions visible to a particular files (not globally visible), so I'll have control over what is polyfilled in each file?
This is only necessary if each file is going to be executed in its own execution context, for example one file is in a web page and another file is in the service worker code. Then code in each file actually refers to different global objects, but TypeScript does not have an adequate way to represent that when the files are compiled together - at compile time (for type declarations), there's only one global scope. In this case, you have to compile each file in its own separate project with separate tsconfig.json
files, and include typings for Array.from
and Object.assign
only where they are needed.