Module
Module in TypeScript has the same semantic meaning as in ES2015.
The actual definition on module is subtle and complex. Fortunately, if you follow these simple rules, creating module is relatively straight forward.
General guideline
- Write module file (with top-level
import
orexport
) and not script file (without top-levelimport
orexport
) - Write module in ES2015 syntax, except
import x = require('x');
.
import keyword
Use
import
keyword instead ofvar
,let
, orconst
withrequire()
.tslint:
no-var-requires
// bad var dr = require('domready'); let dr = require('domready'); const dr = require('domready'); // good import dr = require('domready');
Use
import x = require('x')
syntax for CommonJS packages. do not rely on import interop.Although TypeScript Spec says that
import x = require('x')
andimport * as x from 'x'
is equivalent. But the next statement say "provided the referenced module contains no export assignment". What it means is that they are not the same if the module export a function.import * as x from 'x'
creates an immutable namespace object and it does not work when the package exports a function (module.exports = [function]
).Why? The current interop between CommonJS and ES2015 Module is not consistent across configuration. Using interop today could cause a systematic issue. Avoid it at all cost. Educate your team to learn about the differences and do the right thing.
reference issue: https://github.com/Microsoft/TypeScript/issues/7398
tslint:
no-require-import
// bad import * as dr from 'domready'; // with "commonjs" + "allowSyntheticDefaultImports" or // with "system" import dr from 'domready'; // good import dr = require('domready');
Organize import statements into three sections: 3rd party modules, company modules, and relative (i.e. local) modules.
import _ = require('lodash'); import Promise = require('bluebird'); import { Something } from '@myCompany/somePackage'; import ObjectPage from './ObjectPage';
export keyword
Use ES2015 syntax over
export =
syntax.// Avoid // export = export = function x() { ... }; // Good // ES2015 // default export (import x from './foo') export default function x() { ... } // named export (import { x } from './foo') export function x() { ... }
Use named export. Avoid default export
Why? default export creates conflict if the consumer decide to re-export your module. This applies to internal and external structure.
// bad // src/a/index.ts export default function a() { ... } // src/b/index.ts export default function b() { ... } // src/index.ts export * from './a' export * from './b' // Error
Module keyword
Do not wrap typings in
declare module "X" {
. Expose using top-level import / exportWhy?
declare module "X" {
will cause name conflict if consumer use two different versions of the same library. In TypeScript 1.8, it is used for module augmentation.// bad declare module "X" { export interface A { // stuff... }; } // good export interface A { // stuff... };
Note
Prior to TypeScript 1.5, there are two types of modules:
- Internal module (
declare module X {
) - External module (
declare module "X" {
)
In TypeScript 1.5, the term and keyword namespace
is introduced.
The nomenclature has changed.
- Internal module -> namespace
- External module -> module
The declare module X {
syntax exists for backward compatibility.