TypeScript 2.0
TypeScript 2.0
空和未定义的类型
TypeScript 有两个特殊的类型,Null 和 Undefined,它们分别具有值null
和值undefined
。以前这是不可能明确地命名这些类型,但null
和undefined
现在可以不管的类型检查模式作为类型名称。
类型检查以前被认为null
和undefined
分配到任何东西。有效地,null
和undefined
是每种
类型的有效值,并且不可能特别排除它们(因此不可能检测到它们的错误使用)。
--strictNullChecks
--strictNullChecks
switches to a new strict null checking mode.
在严格的空检查模式下,null
和undefined
值不在
每个类型的域中,并且只能赋予它们自己和any
(undefined
可以赋值的一个例外void
)。所以,而T
和T | undefined
被认为是在常规类型检查模式是同义的(因为undefined
被认为是任何一种亚型T
),它们是不同类型的严格的类型检查模式,并且仅T | undefined
允许undefined
值。T
到 T | null
的关系也是如此。
示例
// Compiled with --strictNullChecks
let x: number;
let y: number | undefined;
let z: number | null | undefined;
x = 1; // Ok
y = 1; // Ok
z = 1; // Ok
x = undefined; // Error
y = undefined; // Ok
z = undefined; // Ok
x = null; // Error
y = null; // Error
z = null; // Ok
x = y; // Error
x = z; // Error
y = x; // Ok
y = z; // Error
z = x; // Ok
z = y; // Ok
分配之前使用检查
在严格的空检查模式下,编译器要求每个引用一个类型的局部变量,undefined
该变量在每个可能的前面的代码路径中都不包含前面的变量。
示例
// Compiled with --strictNullChecks
let x: number;
let y: number | null;
let z: number | undefined;
x; // Error, reference not preceded by assignment
y; // Error, reference not preceded by assignment
z; // Ok
x = 1;
y = null;
x; // Ok
y; // Ok
编译器通过执行基于控制流的类型分析来
检查变量是否明确分配。有关此主题的更多详细信息,请参阅稍后
可选参数和属性
可选参数和属性自动undefined
添加到它们的类型中,即使它们的类型注释没有特别包含undefined
。例如,以下两种类型是相同的:
// Compiled with --strictNullChecks
type T1 = (x?: number) => string; // x has type number | undefined
type T2 = (x?: number | undefined) => string; // x has type number | undefined
非空和非未定义的类型的保护
如果对象或函数的类型包含null
或undefined
,则属性访问或函数调用会产生编译时错误。但是,类型警卫被扩展为支持非空和非未定义的检查。
示例
// Compiled with --strictNullChecks
declare function f(x: number): string;
let x: number | null | undefined;
if (x) {
f(x // Ok, type of x is number here
}
else {
f(x // Error, type of x is number? here
}
let a = x != null ? f(x) : ""; // Type of a is string
let b = x && f(x // Type of b is string | 0 | null | undefined
非空和非不确定型后卫可以使用==
,!=
,===
,或!==
运营商来比较null
或者undefined
,如x != null
或x === undefined
。对主体变量类型的影响准确地反映了 JavaScript 语义(例如,无论指定哪一个值,double-equals(双等) 运算符都检查这两个值,而 triple-equals(三联等) 仅检查指定值)。
类型保护中的虚线名称
以前只能使用类型防护检查局部变量和参数。类型守卫现在支持检查包含变量或参数名称的“虚线名称”,后跟一个或多个属性访问。
示例
interface Options {
location?: {
x?: number;
y?: number;
};
}
function foo(options?: Options) {
if (options && options.location && options.location.x) {
const x = options.location.x; // Type of x is number
}
}
用于虚线名称的类型守护程序也可以与用户定义的类型保护函数以及typeof
和instanceof
运算符一起使用,并且不依赖于--strictNullChecks
编译器选项。
在虚线名称的任何部分分配后,虚线名称的类型保卫不起作用。例如,对于一个型保护x.y.z
将具有分配给以下没有影响x
,x.y
或x.y.z
。
表达式运算符
表达式运算符允许操作数类型包含null
和/或undefined
始终生成非空和非未定义类型的值。
// Compiled with --strictNullChecks
function sum(a: number | null, b: number | null) {
return a + b; // Produces value of type number
}
&&
运算符增加了null
和/或undefined
右操作数,这取决于存在于左操作数的类型的类型,和||
操作者同时删除null
和undefined
从在所得到的联合类型的左操作数的类型。
// Compiled with --strictNullChecks
interface Entity {
name: string;
}
let x: Entity | null;
let s = x && x.name; // s is of type string | null
let y = x || { name: "test" }; // y is of type Entity
类型加宽
在严格的空检查模式下,null
和undefined
类型不会
扩大any
。
let z = null; // Type of z is null
在常规类型检查模式中,推断的类型z
是any
因为扩展,但是在严格的空检查模式下,推断的类型z
是null
(因此,缺少类型注释,null
是唯一可能的值z
)。
非空断言运算符
在类型检查器无法推断该事实的上下文中,可以使用新的!在类型检查器无法推断该事实的上下文中,可以使用新的后修复表达式运算符来断言其操作数为非空且非未定义的。具体来说,该操作x!会生成x带有null和 undefined 的值。类似键入的形式断言<T>x和x as T,所述!非空断言操作者简单地在所发射的JavaScript代码移除。
// Compiled with --strictNullChecks
function validateEntity(e?: Entity) {
// Throw exception if e is null or invalid entity
}
function processEntity(e?: Entity) {
validateEntity(e
let s = e!.name; // Assert that e is non-null and access name
}
兼容性
这些新功能的设计可以用于严格空检查模式和常规检查模式。特别是,在常规类型检查模式下null
,undefined
类型会自动从联合类型中删除(因为它们是所有其他类型的子类型),并且!
允许使用非空断言表达式运算符,但在常规类型检查模式中不起作用。因此,为了向后兼容,更新为使用 null
和 undefined
-aware 类型的声明文件仍可以用于常规类型检查模式。
实际上,严格的空检查模式要求编译中的所有文件都是空的和未定义的。
基于控制流的类型分析
TypeScript 2.0 实现了局部变量和参数的基于控制流的类型分析。以前,对类型警卫进行的类型分析仅限于if
语句和?:
条件表达式,并且不包括分配和控制流结构(例如return
和break
语句)的影响。使用 TypeScript 2.0,类型检查器会分析语句和表达式中所有可能的控制流,以在任何给定位置为声明具有联合类型的局部变量或参数生成最可能的特定类型(缩小类型
)。
示例
function foo(x: string | number | boolean) {
if (typeof x === "string") {
x; // type of x is string here
x = 1;
x; // type of x is number here
}
x; // type of x is number | boolean here
}
function bar(x: string | number) {
if (typeof x === "number") {
return;
}
x; // type of x is string here
}
基于控制流的类型分析在--strictNullChecks
模式中尤其相关,因为可空类型使用联合类型表示:
function test(x: string | null) {
if (x === null) {
return;
}
x; // type of x is string in remainder of function
}
此外,在--strictNullChecks
模式中,基于控制流的类型分析包括对不允许该undefined
值的类型的局部变量的明确分配分析
。
function mumble(check: boolean) {
let x: number; // Type doesn't permit undefined
x; // Error, x is undefined
if (check) {
x = 1;
x; // Ok
}
x; // Error, x is possibly undefined
x = 2;
x; // Ok
}
标记的联合类型
TypeScript 2.0 实现对标记(或区分)联合类型的支持。具体来说,TS 编译器现在支持基于判别属性测试缩小联合类型的类型警卫,并且还将该能力扩展到switch
语句。
示例
interface Square {
kind: "square";
size: number;
}
interface Rectangle {
kind: "rectangle";
width: number;
height: number;
}
interface Circle {
kind: "circle";
radius: number;
}
type Shape = Square | Rectangle | Circle;
function area(s: Shape) {
// In the following switch statement, the type of s is narrowed in each case clause
// according to the value of the discriminant property, thus allowing the other properties
// of that variant to be accessed without a type assertion.
switch (s.kind) {
case "square": return s.size * s.size;
case "rectangle": return s.width * s.height;
case "circle": return Math.PI * s.radius * s.radius;
}
}
function test1(s: Shape) {
if (s.kind === "square") {
s; // Square
}
else {
s; // Rectangle | Circle
}
}
function test2(s: Shape) {
if (s.kind === "square" || s.kind === "rectangle") {
return;
}
s; // Circle
}
一个判别属性型保护
是如下形式的表达x.p == v
,x.p === v
,x.p != v
,或x.p !== v
,其中,p
和v
是一个属性和一个字符串文字类型的表达式或字符串文字类型的联合。判别式p
属性类型的保卫将x
类型缩小为具有与其中一个可能v
值相区别的属性的x
那些组成类型。
请注意,我们目前仅支持字符串文字类型的判别属性。我们打算稍后添加对布尔和数字文字类型的支持。
never类型
TypeScript 2.0 引入了一种新的基元类型never
。never
类型表示永远不会发生的值的类型。具体来说,never
函数的返回类型是永远不会返回的,并且never
是类型保卫下永远不会变为真的变量类型。
never
类型具有以下特征:
never
是每种类型的一种子类型并可分配给每种类型。
- 没有类型是一个子类型或可分配给
never
(除了never
它本身)。
- 在没有返回类型注释的函数表达式或箭头函数中,如果函数没有
return
语句,或者只有return
具有类型表达式的语句never
,并且如果函数的终点不可达(由控制流分析确定),则推断函数的返回类型是never
。
- 在具有显式
never
返回类型注释的函数中,所有return
语句(如果有的话)都必须具有never
类型的表达式,并且该函数的结束点不可访问。
由于never
是每种类型的子类型,因此它总是从联合类型中省略,并且在函数返回类型推断中会被忽略,只要还有其他类型被返回。
返回never
函数的一些示例:
// Function returning never must have unreachable end point
function error(message: string): never {
throw new Error(message
}
// Inferred return type is never
function fail() {
return error("Something failed"
}
// Function returning never must have unreachable end point
function infiniteLoop(): never {
while (true) {
}
}
一些使用函数返回never
的例子:
// Inferred return type is number
function move1(direction: "up" | "down") {
switch (direction) {
case "up":
return 1;
case "down":
return -1;
}
return error("Should never get here"
}
// Inferred return type is number
function move2(direction: "up" | "down") {
return direction === "up" ? 1 :
direction === "down" ? -1 :
error("Should never get here"
}
// Inferred return type is T
function check<T>(x: T | undefined) {
return x || error("Undefined value"
}
由于never
可以分配给每种类型,因此当需要返回never
更具体类型的回调时,可以使用返回的函数:
function test(cb: () => string) {
let s = cb(
return s;
}
test(() => "hello"
test(() => fail()
test(() => { throw new Error( })
只读属性和索引签名
现在可以声明一个属性或索引签名,readonly
修饰符被认为是只读的。
只读属性可能具有初始值设定项,并且可能会在同一个类声明中的构造函数中分配给它,但否则将不允许分配给只读属性。
另外,实体在几种情况下是隐含的
只读:
- 用
get
访问器声明的属性并且没有set
访问者被认为是只读的。
- 在枚举对象的类型中,枚举成员被认为是只读属性。
- 在模块对象的类型中,导出的
const
变量被视为只读属性。
- 在声明中
import
声明的实体被认为是只读的。
- 通过 ES2015 名称空间导入访问的实体被视为只读(例如
foo.x
,foo
声明为只读时为只读import * as foo from "foo"
)。
示例
interface Point {
readonly x: number;
readonly y: number;
}
var p1: Point = { x: 10, y: 20 };
p1.x = 5; // Error, p1.x is read-only
var p2 = { x: 1, y: 1 };
var p3: Point = p2; // Ok, read-only alias for p2
p3.x = 5; // Error, p3.x is read-only
p2.x = 5; // Ok, but also changes p3.x because of aliasing
class Foo {
readonly a = 1;
readonly b: string;
constructor() {
this.b = "hello"; // Assignment permitted in constructor
}
}
let a: Array<number> = [0, 1, 2, 3, 4];
let b: ReadonlyArray<number> = a;
b[5] = 5; // Error, elements are read-only
b.push(5 // Error, no push method (because it mutates array)
b.length = 3; // Error, length is read-only
a = b; // Error, mutating methods are missing
指定this函数的类型
继续指定类或接口的this
类型后,函数和方法现在可以声明this
它们所期望的类型。
默认情况下,this
函数内部的类型是any
。从 TypeScript 2.0 开始,您可以提供一个明确的this
参数。this
参数是在参数列表中首先出现的伪参数:
function f(this: void) {
// make sure `this` is unusable in this standalone function
}
this 参数在回调中
库也可以使用this
参数来声明如何调用回调。
示例
interface UIElement {
addClickListener(onclick: (this: void, e: Event) => void): void;
}
this: void
意味着addClickListener
期望onclick
是一个不需要this
类型的函数。
现在,如果您使用以下方式注释调用代码this
:
class Handler {
info: string;
onClickBad(this: Handler, e: Event) {
// oops, used this here. using this callback would crash at runtime
this.info = e.message;
};
}
let h = new Handler(
uiElement.addClickListener(h.onClickBad // error!
--noImplicitThis
在 TypeScript 2.0 中还添加了一个新标志,用于标记this
函数中的所有用法,而不用明确的类型注释。
全范围支持 tsconfig.json
全范围支持在这里!
Glob-like 文件模式支持两个属性"include"
和"exclude"
。
示例
{
"compilerOptions": {
"module": "commonjs",
"noImplicitAny": true,
"removeComments": true,
"preserveConstEnums": true,
"outFile": "../../built/local/tsc.js",
"sourceMap": true
},
"include": [
"src/**/*"
],
"exclude": [
"node_modules",
"**/*.spec.ts"
]
}
支持的通配符是:
*
匹配零个或多个字符(不包括目录分隔符)
?
匹配任何一个字符(不包括目录分隔符)
**/
递归匹配任何子目录
如果全范围图案的段仅包括*
或.*
包括在内,则仅与支持的扩展名的文件(例如.ts
,.tsx
和.d.ts
在默认情况下.js
和.jsx
如果allowJs
被设置为真)。
如果"files"
和"include"
两者都未指定,则编译器默认为包括所有打字稿(.ts
,.d.ts
和.tsx
)文件在含有目录和子目录,除了那些使用排除"exclude"
属性。如果allowJs
设置为true,JS文件(.js
和.jsx
)也包含在内。
如果指定了"files"
或者 "include"
属性,编译器将会包含这两个属性包含的文件的联合。"outDir"
除非通过"files"
属性显式包含(即使指定了“ exclude
”属性),否则始终排除使用编译器选项指定的目录中的文件。
包含的文件"include"
可以使用"exclude"
属性进行过滤。但是,显式使用该"files"
属性的文件总是包含在内,无论如何"exclude"
。未指定时,该"exclude"
属性默认为排除node_modules
,。bower_components
和jspm_packages
目录
模块解析增强功能:BaseUrl(基础网址),路径映射,rootDirs(根Dirs) 和跟踪
TypeScript 2.0 提供了一组额外的模块解析 knops,以通知
编译器在哪里可以找到给定模块的声明。
有关详细信息,请参阅模块解析
文档
基本网址
在使用AMD模块加载程序的应用程序中,使用一个baseUrl
是常见的做法,其中模块在运行时“部署”到单个文件夹。所有使用非相对名称的模块导入都假定为相对于baseUrl
。
示例
{
"compilerOptions": {
"baseUrl": "./modules"
}
}
现在输入"moduleA"
会在./modules/moduleA
中查找
import A from "moduleA";
路径映射
有时模块不直接位于 baseUrl
下。加载程序使用映射配置在运行时将模块名称映射到文件,请参阅 RequireJs 文档
和 SystemJS 文档
。
TypeScript 编译器支持使用文件中的"paths"
属性声明这种映射tsconfig.json
。
示例
例如,对模块的导入"jquery"
将在运行时转换为"node_modules/jquery/dist/jquery.slim.min.js"
。
{
"compilerOptions": {
"baseUrl": "./node_modules",
"paths": {
"jquery": ["jquery/dist/jquery.slim.min"]
}
}
使用"paths"
还允许更复杂的映射,包括多个后退位置。考虑一个项目配置,其中只有一些模块在一个位置可用,其余的位于另一个位置。
虚拟目录与 rootDirs
使用 'rootDirs',你可以通知编译器组成这个“虚拟”目录的根
; 因此编译器可以解析这些“虚拟”目录中的相关模块导入,就像
在一个目录中合并在一起一样。
示例
鉴于这个项目结构:
src
└── views
└── view1.ts (imports './template1')
└── view2.ts
generated
└── templates
└── views
└── template1.ts (imports './view2')
一个构建步骤将文件复制/src/views
,和/generated/templates/views
在输出相同的目录。在运行时,视图可以期望其模板存在于其旁边,因此应该使用相对名称"./template"
来导入它。
"rootDirs"
指定内容预计在运行时合并的根
的列表。所以按照我们的例子,tsconfig.json
文件应该看起来像这样:
{
"compilerOptions": {
"rootDirs": [
"src/views",
"generated/templates/views"
]
}
}
跟踪模块分辨率
--traceResolution
提供了一种方便的方式来了解编译器如何解决模块问题。
tsc --traceResolution
速记环境模块声明
如果您在使用新模块之前不想花时间写出声明,现在可以使用速记声明快速入门。
declarations.d.ts
declare module "hot-new-module";
所有从速记模块导入的文件都将具有任何类型。
import x, {y} from "hot-new-module";
x(y
模块名称中的通配符
使用模块加载器扩展(例如 AMD
或 SystemJS
)导入非代码资源并不容易; 之前必须为每个资源定义环境模块声明。
TypeScript 2.0 支持使用通配符(*
)来声明模块名称的“族”; 这样,一个扩展只需要一次声明,而不是每个资源。
示例
declare module "*!text" {
const content: string;
export default content;
}
// Some do it the other way around.
declare module "json!*" {
const value: any;
export default value;
}
现在你可以导入匹配"*!text"
或"json!*"
匹配的东西。
import fileContent from "./xyz.txt!text";
import data from "json!http://example.com/data.json";
console.log(data, fileContent
从非类型代码库迁移时,通配符模块名称可能更加有用。结合速记环境模块声明,一组模块可以很容易地声明为any
。
示例
declare module "myLibrary/*";
所有对任何模块的myLibrary
导入都将被编译器视为具有该any
类型;从而关闭对这些模块的形状或类型的检查。
import { readFile } from "myLibrary/fileSystem/readFile`;
readFile( // readFile is 'any'
支持 UMD 模块定义
一些库被设计用于许多模块加载器,或者没有模块加载(全局变量)。这些被称为 UMD
或 同构
模块。这些库可以通过导入或全局变量来访问。
例如:
math-lib.d.ts
export const isPrime(x: number): boolean;
export as namespace mathLib;
该库然后可以用作模块内的导入:
import { isPrime } from "math-lib";
isPrime(2
mathLib.isPrime(2 // ERROR: can't use the global definition from inside a module
它也可以用作全局变量,但只能在脚本中使用。(脚本是一个没有导入或导出的文件。)
mathLib.isPrime(2
可选的类属性
现在可以在类中声明可选的属性和方法,类似于已经在接口中允许的内容。
示例
class Bar {
a: number;
b?: number;
f() {
return 1;
}
g?(): number; // Body of optional method can be omitted
h?() {
return 2;
}
}
在--strictNullChecks模式下编译时,可选属性和方法自动undefined包含在它们的类型中。因此,上面的b属性是类型的number | undefined,上面的g方法是类型的(() => number) | undefined。类型的防护装置可以用来去除undefined部分类型:
function test(x: Bar) {
x.a; // number
x.b; // number | undefined
x.f; // () => number
x.g; // (() => number) | undefined
let f1 = x.f( // number
let g1 = x.g && x.g( // number | undefined
let g2 = x.g ? x.g() : 0; // number
}
私人和受保护的构造函数
类构造函数可能被标记private
或protected
。具有私有构造函数的类不能在类体外实例化,并且不能扩展。具有受保护构造函数的类不能在类体外实例化,但可以扩展。
示例
class Singleton {
private static instance: Singleton;
private constructor() { }
static getInstance() {
if (!Singleton.instance) {
Singleton.instance = new Singleton(
}
return Singleton.instance;
}
}
let e = new Singleton( // Error: constructor of 'Singleton' is private.
let v = Singleton.getInstance(
抽象属性和访问器
抽象类可以声明抽象属性和/或访问器。任何子类都需要声明抽象属性或将其标记为抽象。抽象属性不能有初始化器。抽象访问器不能有整体。
示例
abstract class Base {
abstract name: string;
abstract get value(
abstract set value(v: number
}
class Derived extends Base {
name = "derived";
value = 1;
}
隐式索引签名
如果对象文字中的所有已知属性均可分配给该索引签名,则对象文字类型现在可分配给具有索引签名的类型。这使得可以将用对象字面量初始化为变量的变量传递给需要映射或字典的函数:
function httpService(path: string, headers: { [x: string]: string }) { }
const headers = {
"Content-Type": "application/x-www-form-urlencoded"
};
httpService("", { "Content-Type": "application/x-www-form-urlencoded" } // Ok
httpService("", headers // Now ok, previously wasn't
包含内置类型声明 --lib
获得 ES6 / ES2015 内置 API 声明仅限于target: ES6
。输入--lib
; 与--lib
您可以指定您可以选择包含在您的项目中的内置API声明组列表。例如,如果你期望你的运行时支持Map
,Set
并且Promise
(例如今天最常见的浏览器),那就包括--lib es2015.collection,es2015.promise
。同样,您可以排除不希望包含在项目中的声明,例如,如果正在使用节点项目,则使用DOM --lib es5,es6
。
以下是可用 API 组的列表:
- dom
- webworker
- es5
- es6 / es2015
- es2015.core
- es2015.collection
- es2015.iterable
- es2015.promise
- es2015.proxy
- es2015.reflect
- es2015.generator
- es2015.symbol
- es2015.symbol.wellknown
- es2016
- es2016.array.include
- es2017
- es2017.object
- es2017.sharedmemory
- scripthost
示例
tsc --target es5 --lib es5,es2015.promise
"compilerOptions": {
"lib": ["es5", "es2015.promise"]
}
用--noUnusedParameters和标记未使用的声明--noUnusedLocals
TypeScript 2.0 有两个新的标志来帮助你维护一个干净的代码库。--noUnusedParameters
标记任何未使用的函数或方法参数错误。--noUnusedLocals
标记任何未使用的本地(未导出)声明,如变量,函数,类,导入等...此外,类中未使用的私有成员将--noUnusedLocals
被标记为错误。
示例
import B, { readFile } from "./b";
// ^ Error: `B` declared but never used
readFile(
export function write(message: string, args: string[]) {
// ^^^^ Error: 'arg' declared but never used.
console.log(message
}
名称以参数开头的参数声明_
可免除未使用的参数检查。例如:
function returnNull(_a) { // OK
return null;
}
模块标识符允许.js扩展
在 TypeScript 2.0 之前,模块标识符总是被认为是无扩展的; 例如,在给定导入的情况下,import d from "./moduleA.js"
编译器查找"moduleA.js"
in ./moduleA.js.ts
或的定义./moduleA.js.d.ts
。这使得很难使用像 SystemJS
这样的捆绑/加载工具,它们的模块标识符需要 URI。
使用 TypeScript 2.0,编译器将查找"moduleA.js"
在./moduleA.ts
或./moduleA.d.t
的定义。
使用 'module:es6' 支持 'target:es5'
以前标记为无效标志组合,现在支持target: es5
和 'module:es6'。这应该有助于使用基于 ES2015 的树形图振动器。
函数参数和参数列表中的尾随逗号
函数参数和参数列表中的尾随逗号现在是允许的。这是 Stage-3 ECMAScript 提案的
一个实现,发布到有效的 ES3 / ES5 / ES6。
示例
function foo(
bar: Bar,
baz: Baz, // trailing commas are OK in parameter lists
) {
// Implementation...
}
foo(
bar,
baz, // and in argument lists
新 --skipLibCheck
TypeScript 2.0 添加了一个新的--skipLibCheck
编译器选项,可以跳过声明文件(带扩展名的.d.ts
文件)的类型检查。当程序包含大量声明文件时,编译器将花费大量时间类型检查声明,这些声明已知不包含错误,编译时间可能会因跳过声明文件类型检查而显着缩短。
由于一个文件中的声明会影响其他文件中的类型检查,因此指定--skipLibCheck
因此指定时可能不会检测到某些错误。例如,如果非声明文件扩充了声明文件中声明的类型,则可能会导致仅在检查声明文件时才报告的错误。但是,实际上这种情况很少见。
在整个声明中允许重复标识符
这是重复定义错误的一个常见原因。多个声明文件在接口上定义相同的成员。
TypeScript 2.0 放宽了这个约束,并且允许跨块的重复标识符,只要它们具有相同的
类型。
在同一个块内,重复的定义仍然被禁止。
示例
interface Error {
stack?: string;
}
interface Error {
code?: string;
path?: string;
stack?: string; // OK
}
新 --declarationDir
--declarationDir
允许在与 JavaScript 文件不同的位置生成声明文件。