/// <reference path='path'/> 详解
前置知识
tsconfig.json中有一项配置outFile,该配置用于将所有的输出文件整合成一个文件
在本例中,该项值设定为./result.js,ts 文件编译后的内容将整合于该单一文件中
path 解析方式
path 解析策略同模块解析策略,其有两种:Classic 和 Node
可以通过配置tsconfig.json中的moduleResolution选项值为Classic 或 Node 来选择模块解析策略
问题
我们现有如下代码
// nameC.ts
namespace Validation {
export interface StringValidator {
isAcceptable(s: string): boolean;
}
}
// nameB.ts
namespace Validation {
const lettersRegexp = /^[A-Za-z]+$/;
export class LettersOnlyValidator implements StringValidator {
isAcceptable(s: string) {
return lettersRegexp.test(s);
}
}
}
// nameA.ts
let sv: Validation.StringValidator = {
isAcceptable(s) {
return true;
},
};
let lov: Validation.LettersOnlyValidator =
new Validation.LettersOnlyValidator();
console.log({ sv, lov });
在我们使用tsc进行编译过后,得到的结果如下
// result.js
"use strict";
let sv = {
isAcceptable(s) {
return true;
},
};
let lov = new Validation.LettersOnlyValidator();
console.log({ sv, lov });
var Validation;
(function (Validation) {
const lettersRegexp = /^[A-Za-z]+$/;
class LettersOnlyValidator {
isAcceptable(s) {
return lettersRegexp.test(s);
}
}
Validation.LettersOnlyValidator = LettersOnlyValidator;
})(Validation || (Validation = {}));
我们运行该 js 文件,发现报错:
我们发现,类LettersOnlyValidator在其被定义前就已经使用了,这说明了一个问题
输出文件内容的顺序不对,我们发现nameA.ts文件的内容先被解析了,这是不符合我们预期的!
解决办法
ts为我们提供了三斜线指令 /// <reference path='path'/>来让我们指定文件解析的先后顺序
我们将ts文件内容添加上三斜线指令之后,内容如下
// nameC.ts
namespace Validation {
export interface StringValidator {
isAcceptable(s: string): boolean;
}
}
// nameB.ts
/// <reference path='nameC.ts' />
namespace Validation {
const lettersRegexp = /^[A-Za-z]+$/;
export class LettersOnlyValidator implements StringValidator {
isAcceptable(s: string) {
return lettersRegexp.test(s);
}
}
}
// nameA.ts
/// <reference path='nameC.ts' />
/// <reference path='nameB.ts' />
let sv:Validation.StringValidator = {
isAcceptable(s) {
return true
},
}
let lov:Validation.LettersOnlyValidator = new Validation.LettersOnlyValidator()
console.log({sv,lov})
当文件内使用到了另一个文件内命名空间的内容时,需要在该文件内使用三斜线指令 来告诉编译器你引用的内容所在的位置,后继编译器就会根据三斜线指令的引用关系, 来决定整合文件的内容的顺序
编译后结果如下
// result.js
"use strict";
/// <reference path='nameC.ts' />
var Validation;
/// <reference path='nameC.ts' />
(function (Validation) {
const lettersRegexp = /^[A-Za-z]+$/;
class LettersOnlyValidator {
isAcceptable(s) {
return lettersRegexp.test(s);
}
}
Validation.LettersOnlyValidator = LettersOnlyValidator;
})(Validation || (Validation = {}));
/// <reference path='nameC.ts' />
/// <reference path='nameB.ts' />
let sv = {
isAcceptable(s) {
return true;
},
};
let lov = new Validation.LettersOnlyValidator();
console.log({ sv, lov });
运行result.js文件,发现能够正常运行并输出期望结果
解决逻辑
编译器在编译文件时有个步骤——预处理输入文件,该步骤会对输入文件预处理来解析所有三斜线指令,在这个过程中额外的文件会被加入到编译过程中。
以上述代码为例,编译器在解析nameA.ts文件前,会先监测其文件内容有没有三斜线指令,结果发现有
/// <reference path='nameC.ts' />
/// <reference path='nameB.ts' />
那么编译器会先去对nameC.ts进行编译,发现nameC.ts中没有三斜线指令,则先将编译结果添加到输出文件中,后面继续解析nameB.ts 发现nameB.ts也使用三斜线指令指向了nameA.ts,不过其之前已经编译过,就不再处理,而是将nameB.ts内容编译后添加到输出文件中, 此时nameC.ts的三斜线指令已经预处理完毕,那么就将nameC.ts内容编译后添加到输出文件中,此时输出文件的内容便是我们预期的输出结果。
说大白话就是,/// <reference path='nameC.ts' />就是告诉编译器:我这里使用了nameC.ts的内容,请你先去编译nameC.ts的内容后再来我这
作用
根据上述过程我们得出一个结论:
/// <reference path='path' />的作用就是当需要将ts文件编译结果整合为一个文件时,我们可以使用该三斜线指令来告诉编译器这些文件内容的依赖关系,从而使得编译输出结果符合我们的预期
#### 注意事项
- 一个三斜线引用路径是相对于包含它的文件的
- 如果在tsconfig.json中的noResolve值为true,则会忽略所有三斜线指令
别的解决办法
ts会影响输出文件内容顺序的方式有2:三斜线指令和import的文件顺序
现我们使用import来影响文件顺序,我们需要关闭outFile属性
我们将上述ts文件内容调整如下
// nameC.ts
export namespace Validation {
export interface StringValidator {
isAcceptable(s: string): boolean;
}
}
// nameB.ts
import {Validation as nameC} from "./nameC"
export namespace Validation {
const lettersRegexp = /^[A-Za-z]+$/;
export class LettersOnlyValidator implements nameC.StringValidator {
isAcceptable(s: string) {
return lettersRegexp.test(s);
}
}
}
// nameA.ts
import {Validation as nameC} from "./nameC"
import {Validation as nameB} from "./nameB"
let sv:nameC.StringValidator = {
isAcceptable(s) {
return true
},
}
let lov:nameB.LettersOnlyValidator = new nameB.LettersOnlyValidator()
console.log({sv,lov})
其输出结果如下,此时tsconfig.json的module值为CommonJS
// nameC.js
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
// nameB.js
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Validation = void 0;
var Validation;
(function (Validation) {
const lettersRegexp = /^[A-Za-z]+$/;
class LettersOnlyValidator {
isAcceptable(s) {
return lettersRegexp.test(s);
}
}
Validation.LettersOnlyValidator = LettersOnlyValidator;
})(Validation = exports.Validation || (exports.Validation = {}));
// nameA.js
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const nameB_1 = require("./nameB");
let sv = {
isAcceptable(s) {
return true;
},
};
let lov = new nameB_1.Validation.LettersOnlyValidator();
console.log({ sv, lov });
我们运行 nameA.js 发现达到输出预期,我们成功使用了三斜线指令之外的方式来影响 输出文件的内容