TypeScript之前处理CommonJS/AMD/UMD模块的命名空间导入(如import * as foo from "foo")时等同于const foo = require("foo")。这样做很简单,但如果引入的主要对象(比如这里的foo)是基本类型、类或者函数,就有问题。ECMAScript标准规定了命名空间记录是一个纯粹的对象,并且引入的命名空间(比如前面的foo)应该是不可调用的,然而在TypeScript却中可以。
同样地,一个CommonJS/AMD/UMD模块的默认导入(如import d from "foo")被处理成等同于 const d = require("foo").default的形式。然而现在大多数可用的CommonJS/AMD/UMD模块并没有默认导出,导致这种引入语句在实践中不适用于非ES模块。比如 import fs from "fs" or import express from "express" 都不可用。
在使用标签--esModuleInterop后,这两个问题都得到了解决:
命名空间导入(如import * as foo from "foo")的对象现在被修正为不可调用的。调用会报错。
对CommonJS/AMD/UMD模块可以使用默认导入(如import d from "foo")且能正常工作了。
注: 这个新特性有可能对现有的代码产生破坏,因此以标记的方式引入。但无论是新项目还是之前的项目,我们都强烈建议使用它。对于之前的项目,命名空间导入 (import * as express from "express"; express();) 需要被改写成默认引入 (import express from "express"; express();).
例子
使用 --esModuleInterop 后,会生成两个新的辅助量 __importStar and __importDefault ,分别对应导入*和导入default,比如这样的输入:
const Foo = "Foo";
const Bar = "Bar";
let x = {
[Foo]: 100,
[Bar]: "hello",
};
let a = x[Foo]; // a类型为'number'; 在之前版本,类型为'number | string',现在可以追踪到类型
let b = x[Bar]; // b类型为'string';
// Works
declare const Foo: unique symbol;
// Error! 'Bar'不是const声明的
let Bar: unique symbol = Symbol();
// Works - 对变量Foo的引用,它的声明身份与Foo绑定
let Baz: typeof Foo = Foo;
// Also works.
class C {
static readonly StaticSymbol: unique symbol = Symbol();
}
const Foo = Symbol();
const Bar = Symbol();
// Error: 不能比较两个unique symbols.
if (Foo === Bar) {
// ...
}
class C {
foo: number;
bar = "hello";
baz: boolean;
// ~~~
// Error! Property 'baz' has no initializer and is not assigned directly in the constructor.
constructor() {
this.foo = 42;
}
}
class C {
foo!: number;
// ^
// Notice this exclamation point!
// This is the "definite assignment assertion" modifier.
constructor() {
this.initialize();
}
initialize() {
this.foo = 0;
}
}
let x: number;
initialize();
console.log(x + x);
// ~ ~
// Error! Variable 'x' is used before being assigned.
function initialize() {
x = 10;
}
// Notice the '!'
let x!: number;
initialize();
// No error!
console.log(x + x);
function initialize() {
x = 10;
}
let x: number;
initialize();
// No error!
console.log(x! + x!);
function initialize() {
x = 10;
declare function f<T>(...items: T[]): T;
// let obj: { a: number, b: number } |
// { a: string, b?: undefined } |
// { a?: undefined, b?: undefined }
let obj = f({ a: 1, b: 2 }, { a: "abc" }, {});
obj.a; // string | number | undefined
obj.b; // number | undefined
class A {}
class B extends A {}
class C extends A {}
class D extends A { c: string }
class E extends D {}
let x1 = !true ? new A() : new B(); // A
let x2 = !true ? new B() : new C(); // B | C (previously B)
let x3 = !true ? new C() : new D(); // C | D (previously C)
let a1 = [new A(), new B(), new C(), new D(), new E()]; // A[]
let a2 = [new B(), new C(), new D(), new E()]; // (B | C | D)[] (previously B[])
function f1(x: B | C | D) {
if (x instanceof B) {
x; // B (previously B | D)
}
else if (x instanceof C) {
x; // C
}
else {
x; // D (previously never)
}
}
interface A { a: number };
interface B { b: string };
function foo(x: A | B) {
if ("a" in x) {
return x.a;
}
return x.b; // 此时x的类型推断为B, 属性a不存在
}
import * as foo from "foo";
import b from "bar";
"use strict";
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
result["default"] = mod;
return result;
}
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
}
exports.__esModule = true;
var foo = __importStar(require("foo"));
var bar_1 = __importDefault(require("bar"));
const million = 1_000_000;
const phone = 555_734_2231;
const bytes = 0xFF_0C_00_FF;
const word = 0b1100_0011_1101_0001;