likes
comments
collection
share

TS三斜线指令

作者站长头像
站长
· 阅读数 44

要点

  1. 三斜线指令用主要用于声明文件&文件(/// <reference path/>)、文件&第三方模块(/// <reference types/>)之间的依赖关系
  2. 三斜线指令最好只出现在你手动编写声明文件(即.d.ts文件)的场景
  3. /// <reference path/>还有用于控制输出文件内容顺序的作用
  4. 三斜线指令的引用路径是相对于包含它的文件的
  5. 三斜线指令出现在非声明文件的情况
    1. 同namespace一起出现:用于控制输出文件内容顺序,链接
    2. 同外部模块声明(Ambient Module Declarations)一起出现,告诉编译器当前文件对该模块存在依赖关系,并在文件内引入模块的类型声明以便使用,不过当该模块类型声明文件被tsconfig.json已经引入时,这时三斜线指令不是必须的,链接

三斜线指令 /// <reference path/>控制输出文件内容顺序原理

前置知识

  1. tsconfig.json中有一项配置outFile,该配置用于将所有的输出文件整合成一个文件 在本例中,该项值设定为./result.js,ts 文件编译后的内容将整合于该单一文件中
  2. 三斜线的路径解析策略:解析策略同模块解析策略,其有两种:ClassicNode,可以通过配置tsconfig.json中的moduleResolution选项值为ClassicNode 来选择解析策略

问题

我们现有如下代码

// 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 文件,发现报错:

`Class 'LettersOnlyValidator' used before its declaration.`

我们发现,类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的内容后再来我这

如果有帮到您的话,请点个赞吧~ 谢谢!

转载自:https://juejin.cn/post/7368692841298804790
评论
请登录