函数中this
的默认类型是any
。从TypeScript 2.0开始,你可以提供一个明确的this
参数。this
参数是伪参数,它位于函数参数列表的第一位:
function f(this: void) {
// 确保`this`在这个独立的函数中无法使用
}
回调函数中的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) {
// 哎哟,在这里使用this.在运行中使用这个回调函数将会崩溃。
this.info = e.message;
};
}
let h = new Handler();
uiElement.addClickListener(h.onClickBad); // 错误!
--noImplicitThis
TypeScript 2.0还增加了一个新的编译选项用来标记函数中所有没有明确类型注释的this
的使用。
tsconfig.json
支持文件通配符
文件通配符来啦!!支持文件通配符一直是最需要的特性之一。
类似文件通配符的文件模式支持两个属性"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
,如果allowJs
设置为true
,.js
和.jsx
也属于默认)。
如果"files"
和"include"
都没有指定,编译器默认包含所有目录中的TypeScript文件(.ts
、.d.ts
和.tsx
),除了那些使用exclude
属性排除的文件外。如果allowJs
设置为true,JS文件(.js
和.jsx
)也会被包含进去。
如果"files"
和"include"
都指定了,编译器将包含这两个属性指定文件的并集。使用ourDir
编译选项指定的目录文件总是被排除,即使"exclude"
属性指定的文件也会被删除,但是files
属性指定的文件不会排除。
"exclude"
属性指定的文件会对"include"
属性指定的文件过滤。但是对"files"
指定的文件没有任何作用。当没有明确指定时,"exclude"
属性默认会排除node_modules
、bower_components
和jspm_packages
目录。
模块解析增加:BaseUrl、路径映射、rootDirs和追踪
TypeScript 2.0提供了一系列额外的模块解析属性告诉编译器去哪里可以找到给定模块的声明。
更多详情,请参阅模块解析文档。
Base URL
使用了AMD模块加载器并且模块在运行时”部署“到单文件夹的应用程序中使用baseUrl
是一种常用的做法。所有非相对名称的模块导入被认为是相对于baseUrl
的。
示例
{
"compilerOptions": {
"baseUrl": "./modules"
}
}
现在导入moduleA
将会在./modules/moduleA
中查找。
import A from "moduleA";
路径映射
有时模块没有直接位于baseUrl中。加载器使用映射配置在运行时去映射模块名称和文件,请参阅RequireJs文档和SystemJS文档。
TypeScript编译器支持tsconfig
文件中使用"paths"
属性映射的声明。
示例
例如,导入"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"
导入。
"rootDir"
指定的一组根目录的内容将会在运行时合并。因此在我们的例子,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是'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); // 错误:无法在模块内部使用全局定义
它也可以被用来作为一个全局变量,只限于没有import
和export
脚本文件中。
mathLib.isPrime(2);
可选类属性
现在可以在类中声明可选属性和方法,与接口类似。
示例
class Bar {
a: number;
b?: number;
f() {
return 1;
}
g?(): number; // 可选方法的方法体可以省略
h?() {
return 2;
}
}
在--strictNullChecks
模式下编译时,可选属性和方法会自动添加undefined
到它们的类型中。因此,上面的b
属性类型是number | undefined
,上面g
方法的类型是(()=> number) | undefined
。使用类型保护可以去除undefined
。
私有的和受保护的构造函数
类的构造函数可以被标记为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(); // 错误:Singleton的构造函数是私有的。
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;
}
隐式索引签名
如果对象字面量中所有已知的属性是赋值给索引签名,那么现在对象字面量类型可以赋值给索引签名类型。这使得一个使用对象字面量初始化的变量作为参数传递给期望参数是map或dictionary的函数成为可能:
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" }); // 可以
httpService("", headers); // 现在可以,以前不可以。
使用--lib
编译参数包含内置类型声明
获取ES6/ES2015内置API声明仅限于target: ES6
。输入--lib
,你可以使用--lib
指定一组项目所需要的内置API。比如说,如果你希望项目运行时支持Map
、Set
和Promise
(例如现在静默更新浏览器),直接写--lib es2015.collection,es2015.promise
就好了。同样,你也可以排除项目中不需要的声明,例如在node项目中使用--lib es5,es6
排除DOM。
下面是列出了可用的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";
// ^ 错误:`B`声明了,但是没有使用。
readFile();
export function write(message: string, args: string[]) {
// ^^^^ 错误:'arg'声明了,但是没有使用。
console.log(message);
}
使用以_
开头命名的参数声明不会被未使用参数检查。例如:
function returnNull(_a) { // 正确
return null;
}
模块名称允许.js
扩展名
TypeScript 2.0之前,模块名称总是被认为是没有扩展名的。例如,导入一个模块import d from "./moduleA.js"
,则编译器在./moduleA.js.ts
或./moduleA.js.d.ts
中查找"moduleA.js"
的定义。这使得像 SystemJS这种期望模块名称是URI的打包或加载工具很难使用。
使用TypeScript 2.0,编译器将在./moduleA.ts
或./moduleA.d.ts
中查找"moduleA.js"
的定义。
支持编译参数target : es5
和module: es6
同时使用
之前编译参数target : es5
和module: es6
同时使用被认为是无效的,但是现在是有效的。这将有助于使用基于ES2015的tree-shaking(将无用代码移除)比如 rollup。
函数形参和实参列表末尾支持逗号
现在函数形参和实参列表末尾允许有逗号。这是对第三阶段的ECMAScript提案的实现, 并且会编译为可用的 ES3/ES5/ES6。
示例
function foo(
bar: Bar,
baz: Baz, // 形参列表末尾添加逗号是没有问题的。
) {
// 具体实现……
}
foo(
bar,
baz, // 实参列表末尾添加逗号同样没有问题
);
新编译参数--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文件不在同一个位置中。
标签:TypeScript
相关阅读 >>
简单对比,看看typescript中interface和type间的区别
更多相关阅读请进入《typescript》频道 >>

Vue.js 设计与实现 基于Vue.js 3 深入解析Vue.js 设计细节
本书对 Vue.js 3 技术细节的分析非常可靠,对于需要深入理解 Vue.js 3 的用户会有很大的帮助。——尤雨溪,Vue.js作者