TypeScript2

模块

  1. m模块在自身作用域执行,不是全局作用域
  2. 除非使用export导出,否则定义在内部的变量都是不可见的
  3. 模块使用模块加载器导入其他模块,
    1. 模块加载器用于在执行此模块代码之前去查找并执行这个模块所有的依赖
    2. JavaScript模块加载器是服务于Node.js的CommonJS和服务于Web应用的Require.js
  4. 任何包含顶级import或者export的文件都为一个模块,如果都不带,那么内容为全局可见的

导出

导出语句

1
2
export { ZipCodeValidator };
export { ZipCodeValidator as mainValidator };

使用as可以重命名

重新导出

1
2
3
4
5
6
7
8
export class ParseIntBasedZipCodeValidator {
isAcceptable(s: string) {
return s.length === 5 && parseInt(s).toString() === s;
}
}

// 导出原先的验证器但做了重命名
export {ZipCodeValidator as RegExpBasedZipCodeValidator} from "./ZipCodeValidator";
  1. 不会在当前模块导入或者定义一个新的局部变量

导入

1
2
3
4
5
import { ZipCodeValidator } from "./ZipCodeValidator"; //普通导入
import { ZipCodeValidator as ZCV } from "./ZipCodeValidator"; //重命名

import * as validator from "./ZipCodeValidator"; //导入全部,用validator代表这个模块
let myValidator = new validator.ZipCodeValidator();

默认导出

  1. 类和函数声明可以直接被标记为默认导出。 标记为默认导出的类和函数的名字是可以省略的。

    1
    2
    3
    4
    5
    const numberRegexp = /^[0-9]+$/;

    export default function (s: string) {
    return s.length === 5 && numberRegexp.test(s);
    }
  2. default的导出可以是一个值

export = 和 import = require()

  1. 在CommonJS和AMD的环境里有一个exports变量,包含着一个模块的所有导出内容
  2. exports可以被赋值为一个对象,类似于es6的默认导出
  3. 为了兼容CommonJS和AMD提供了export = 语法
  4. 若使用export =导出一个模块,则必须使用TypeScript的特定语法import module = require("module")来导入此模块。
1
2
3
4
5
6
7
//test.ts
export = {
hello: 'world'
}

///导入
import zip = require("./test.ts");

命名空间 nameSpace

“内部模块”

  1. 提取逻辑,记录的同时不担心与其他对象产生命名冲突
  2. 相同namespace即使在不同文件也是同一个明明空间,与接口的合并同工
  3. 简化命名空间操作: import q = x.y.z (用q代表x.y.z,起个名字)

模块和命名空间的区别

  1. 模块有自己的作用域,只有被导出才能被其他文件引用。namespace可以在全局引用,即使不导出

    1
    2
    3
    4
    5
    6
    7
    8
    9
    // global.ts
    namespace MyNamespace {
    export class MyClass {
    constructor(public name: string) {}
    }
    }

    // main.ts
    let instance = new MyNamespace.MyClass('example');
  2. 模块是文件级别的,用import和export进行管理,namespace在全局通过代码自行组织

  3. 使用场景:模块是 TypeScript 和 ES6 中推荐的组织代码的方式,适用于大型项目。而命名空间主要用于组织全局变量,适用于较小的项目或者为了向后兼容

ps:可以把模块比作每个城池,命名空间比作教会

模块解析

相对导入

  1. 以 / ./ ../ 开头的
  2. 相对于导入他的文件

非相对

  1. 所有其它形式的导入
  2. 相对于baseUrl或者路径映射

模块解析策略

Classic

  1. 之前的默认解析策略,为了向后兼容而保留

  2. 相对导入模块相对于导入他的文件解析

    1
    2
    3
    4
    /root/src/folder/A.ts

    /root/src/folder/moduleB.ts
    /root/src/folder/moduleB.d.ts
  3. 非相对模块的导入,编译器会从包含文件的目录依次向上级目录遍历

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    //有一个对moduleB的非相对导入
    import { b } from "moduleB"
    //它是在/root/src/folder/A.ts文件里,会以如下的方式来定位"moduleB":

    /root/src/folder/moduleB.ts
    /root/src/folder/moduleB.d.ts
    /root/src/moduleB.ts
    /root/src/moduleB.d.ts
    /root/moduleB.ts
    /root/moduleB.d.ts
    /moduleB.ts
    /moduleB.d.ts

Node

相对路径:

  1. 相对路径文件是否存在

  2. 如果不存在文件,检查是否有目录,如果有目录,检查是否【有package.json文件并且这个文件制定了一个main路径】,如果发现了package.json中有main路径,则会引用 检查目录的路径拼接main路径

    1
    2
    3
    /root/src/moduleB/package.json
    包含了{ "main": "lib/mainModule.js" },
    那么Node.js会引用/root/src/moduleB/lib/mainModule.js
  3. 检查/root/src/moduleB目录是否包含一个index.js文件。 这个文件会被隐式地当作那个文件夹下的”main”模块。

非相对路径:

  1. 会在node_modules里面查找
  2. 假设require(”moduleB”)
  3. 首先会查找 /node_modules/moduleB.js
  4. /node_modules/moduleB/package.json(如果指定了main属性)
  5. /node_module/moduleB/index.js
  6. 之后向上跳目录

Untitled

附加的模块解析标记

由于工程源码和输出结构不同,需要经过转换。

包括把ts编译为js,将不同位置的依赖拷贝至一个输出位置。

最终结果就是:

运行时的模块名与包含它们声明的源文件里的模块名不同。 或者最终输出文件里的模块路径与编译时的源文件路径不同了。

Base URL

设置baseUrl来告诉编译器到哪里去查找模块。 所有非相对模块导入都会被当做相对于baseUrl

baseUrl的值由以下两者之一决定:

  • 命令行中baseUrl的值(如果给定的路径是相对的,那么将相对于当前路径进行计算)
  • ‘tsconfig.json’里的baseUrl属性(如果给定的路径是相对的,那么将相对于‘tsconfig.json’路径进行计算)

注意相对模块的导入不会被设置的baseUrl所影响,因为它们总是相对于导入它们的文件

路径映射

TypeScript编译器通过使用tsconfig.json文件里的"paths"来支持的声明映射。 下面是一个如何指定jquery"paths"的例子。

1
2
3
4
5
6
7
8
{
"compilerOptions": {
"baseUrl": ".", // This must be specified if "paths" is.
"paths": {
"jquery": ["node_modules/jquery/dist/jquery"] // 此处映射是相对于"baseUrl"
}
}
}

path是相对于baseUrl的

(如果是非相对名,则直接拼接,如果是相对名,则进行对应的改变)

装饰器

装饰器是实验特性

启用:

1
2
3
4
5
6
{
"compilerOptions": {
"target": "ES5",
"experimentalDecorators": true
}
}

定义:它能够被附加到类声明方法访问符属性参数上。 装饰器使用 @expression这种形式,expression求值后必须为一个函数,它会在运行时被调用,被装饰的声明信息做为参数传入。

装饰器工厂

Untitled

类装饰器

Untitled

  1. target此处指Person

属性装饰器

Untitled

  1. target为Person类
  2. attr 为 ”name“

方法装饰器

Untitled

  1. target为Person类
  2. propertyKey为方法的属性键值
  3. descriptor为属性描述

Untitled

装饰器组合

多个装饰器应用在一个生命上时

  1. 由上到下对装饰器表达式求知(即获取返回的函数部分)
  2. 由下到上把函数结果进行调用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function f() {
console.log("f(): evaluated");
return function (target, propertyKey: string, descriptor: PropertyDescriptor) {
console.log("f(): called");
}
}

function g() {
console.log("g(): evaluated");
return function (target, propertyKey: string, descriptor: PropertyDescriptor) {
console.log("g(): called");
}
}

class C {
@f()
@g()
method() {}
}

在控制台里会打印出如下结果:

1
2
3
4
f(): evaluated
g(): evaluated
g(): called
f(): called

装饰器求值

原则:由内到外,由小到大

类中不同声明上的装饰器将按以下规定的顺序应用:

  1. 参数装饰器,然后依次是方法装饰器访问符装饰器,或属性装饰器应用到每个实例成员。
  2. 参数装饰器,然后依次是方法装饰器访问符装饰器,或属性装饰器应用到每个静态成员。
  3. 参数装饰器应用到构造函数。
  4. 类装饰器应用到类。

Declare

告诉编译器这个变量是存在的

应用于一些不使用ts的库,但是需要引用到ts里面,需要有一个类型,就可以用declare来声明一下

1
2
3
4
5
6
7
8
9
10
11
//index.js
function showMessage(message){
console.log(message)
}

//test.ts
showMessage("name")

//common.d.ts
//如果删去则报错
declare showMessage(message:string):void

引入类型描述的时候会自动引入对应的js的文件

同名引入

  • 对于自己写的模块,会默认引用与该文件同名的类型声明文件。比如有一个foo.js和foo.d.ts,如果使用import会自动由foo.d.ts提供

自动引用

  • 第三方库都有@types/xxx ,declare文件

    Untitled

    配置这个,type在的目录

declare module、namespace

1
2
3
4
5
declare module "jquery" {
export let fn:string
}

jquery.fn

为外部属性新增

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//index.ts
export interface A {
value1:string
}

//test.ts
import {A} from "index.ts"
declare module "index.ts" {
interface A {
value2:string
}
}

//接口合并

三斜线指令

包含单个XML标签的单行注释,注释的内容会作为编译器指令使用

只能仿造包含他的文件的最顶端

其前面只能出现单行或者多行注释

如果前面有语句或声明,那么会被当做普通的单行注释

1
/// <reference path="..." />

专门用来引用.d.ts文件

1
/// <reference types="..." />

预处理

解析所有三斜线引用的指令,把对应的文件添加到编译的过程中

路径是相对于包含他的文件的

如果引用的的文件不存在会报错

—noResolve

如果使用了noResolve,三斜线会被忽略

1
2
3
4
5
/// <reference path="..." /> 声明对文件的依赖

/// <reference types="node" /> 声明对包的依赖
//表明使用了@types/node/index.d.ts里面生命的名字

在编译阶段生成的声明文件,编译器会自动添加 ///

Untitled

配置这个,type在的目录