I have a website that uses a large typescript code base. All clases as in their own files, and wrapped with an internal module like so:
file BaseClass.ts
module my.module {
export class BaseClass {
}
}
file ChildClass.ts
module my.module {
export ChildClass extends my.module.BaseClass {
}
}
All file are included globally with script tags, in the appropriate order (using ASP.NET Bundling).
I would like to move to a more modern setup and use webpack. I would like my module syntax to use whatever the new ECMASCRIPT module standard is. But there is much code using the existing "module namespaces" so I would like an update path that will support this type of code -
let x = new my.module.ChildClass();
So I think I need to have something like this -
import * as my.module from ???;
Or use namespaces?
However, if that is not best practices, I would like to stick with best practices. The internal modules are currently very helpful for organizing the different application layers and services...
How would I accomplish this since the "module" is across many files? Really, all I am trying to accomplish is to have a namespace, and get away from global scripts.
Disclaimer (this is not meant as a comprehensive guide but rather as a conceptual starting point. I hope to demonstrate the feasibility of migration, but ultimately it involves a fair amount of hard work)
I've done this in a large enterprise project. It was not fun, but it worked.
Some tips:
Only keep the global namespace object(s) around for as long as you need them.
Start at the leaves of your source, converting files which have no dependents into external modules.
Although these files will themselves rely on the global namespace object(s) you have been using, this will not be a problem if you work carefully from the outside in.
Say you have a global namespace like
utilsand it is spread across 3 files as followsNow imagine you have another globally scoped namespaced file only, this time, we can easily break it out into a proper module because it does not depend on any other members of its own namespace. For example I will use a service that queries for weather info at random locations around the globe using the stuff from
utils.No we are going to turn our
services.weatherSerciceglobal, namespaced constant into a proper external module and it will be fairly easy in this caseCommon Pitfalls and Workarounds:
A snag can occur if we need to reference the functionality of this newly modulified code from one of our existing global namespaces
Since we are now using modules for at least some part of our code, we have a module loader or bundler in play (If you writing for NodeJS, i.e an express application you can disregard this as the platform integrates a loader, but you can also use a custom loader). That module loader or bundler could be SystemJS, RequireJS, Webpack, Browserify, or something more esoteric.
The biggest, and most common mistake is to have something like this
And, as that no longer works, we write this broken code instead
The above code is broken because, simply by adding an
import... from '...'statement (the same applies toimport ... = require(...)) we have turnedappinto a module accidentally, before we were ready.So, we need a workaround. Temporarily, return to the
servicesdirectory and add a new Module, here calledweather-service.shim.tsThen, change
app.tstoNote, this should not be done unless you need to. Try to organize you conversion to modules so as to minimize this.
Remarks:
In order to correctly perform this gradual migration it is important to understand precisely what defines what is and what is not a module.
This is determined by language parsers at the source level for each file.
When an ECMAScript file is parsed, there are two possible goal symbols, Script and Module.
https://tc39.es/ecma262/multipage/notational-conventions.html#sec-syntactic-grammar
Hand-wavingly, a Script is a global. Code written using TypeScript's internal modules, always falls into this category.
A source file is a Module when and only when it contains one or more top level
importorexportstatements*. TypeScript used to refer to such sources as external modules but they are now known simply as modules in order to match the ECMAScript specification's terminology.Here are some source examples of scripts and modules. Note that how they are differentiated is subtle yet well-defined.
square.ts --> Script
now.ts --> Script
square.ts --> Module
bootstrap.ts --> Module
bootstrap.ts --> Script