模块
allowArbitraryExtensions
- 发布版本: 5.0
在 TypeScript 5.0 中,当导入路径以非已知 JavaScript 或 TypeScript 文件扩展名结尾时,编译器将查找该路径的声明文件,形式为 {文件基名}.d.{扩展名}.ts。 例如,如果你在打包器项目中使用 CSS 加载器,你可能想要为这些样式表编写(或生成)声明文件:
/* app.css */
.cookie-banner {
display: none;
}// app.d.css.ts
declare const css: {
cookieBanner: string;
};
export default css;// App.tsx
import styles from "./app.css";
styles.cookieBanner; // string默认情况下,此导入将引发错误,让你知道 TypeScript 不理解此文件类型,并且你的运行时可能不支持导入它。 但是,如果你已将运行时或打包器配置为处理它,则可以使用新的 --allowArbitraryExtensions 编译器选项抑制错误。
请注意,历史上,通常可以通过添加名为 app.css.d.ts 而不是 app.d.css.ts 的声明文件来实现类似的效果——然而,这只是通过 Node 的 CommonJS require 解析规则实现的。 严格来说,前者被解释为名为 app.css.js 的 JavaScript 文件的声明文件。 由于相对文件导入需要在 Node 的 ESM 支持中包含扩展名,因此在 --moduleResolution node16 或 nodenext 下的 ESM 文件中,TypeScript 会对我们的示例报错。
allowImportingTsExtensions
默认值: 如果 rewriteRelativeImportExtensions 为
true,则为true;否则为false。发布版本: 5.0
--allowImportingTsExtensions 允许 TypeScript 文件使用 TypeScript 特定的扩展名(如 .ts、.mts 或 .tsx)相互导入。
此标志仅在启用 --noEmit 或 --emitDeclarationOnly 时才允许,因为这些导入路径在 JavaScript 输出文件中无法在运行时解析。 这里的期望是你的解析器(例如你的打包器、运行时或其他工具)将使这些 .ts 文件之间的导入工作。
allowUmdGlobalAccess
- 发布版本: 3.5
当设置为 true 时,allowUmdGlobalAccess 允许你从模块文件内部将 UMD 导出作为全局变量访问。模块文件是具有导入和/或导出的文件。如果没有此标志,使用 UMD 模块的导出需要导入声明。
此标志的一个用例是 Web 项目,你知道特定库(如 jQuery 或 Lodash)在运行时始终可用,但你无法通过导入访问它。
baseUrl
- 发布版本: 2.0
设置一个基本目录,从中解析裸说明符模块名称。例如,在目录结构中:
project
├── ex.ts
├── hello
│ └── world.ts
└── tsconfig.json使用 "baseUrl": "./",TypeScript 将从 tsconfig.json 所在的同一文件夹开始查找文件:
import { helloWorld } from "hello/world";
console.log(helloWorld);此解析的优先级高于从 node_modules 的查找。
此功能设计用于与浏览器中的 AMD 模块加载器结合使用,不建议在任何其他上下文中使用。从 TypeScript 4.1 开始,使用 paths 时不再需要设置 baseUrl。
customConditions
--customConditions 接受一个额外的条件列表,当 TypeScript 从 package.json 的 exports 或 imports 字段解析时,这些条件应该成功。 这些条件会被添加到解析器默认使用的任何现有条件中。
例如,当在 tsconfig.json 中这样设置此字段时:
{
"compilerOptions": {
"target": "es2022",
"moduleResolution": "bundler",
"customConditions": ["my-condition"]
}
}每当在 package.json 中引用 exports 或 imports 字段时,TypeScript 将考虑名为 my-condition 的条件。
因此,当从具有以下 package.json 的包导入时
{
// ...
"exports": {
".": {
"my-condition": "./foo.mjs",
"node": "./bar.mjs",
"import": "./baz.mjs",
"require": "./biz.mjs"
}
}
}TypeScript 将尝试查找对应于 foo.mjs 的文件。
此字段仅在 --moduleResolution 的 node16、nodenext 和 bundler 选项下有效。
module
允许的值:
none,commonjs,amd,umd,system,es6/es2015,es2020,es2022,esnext,node16,node18,node20,nodenext,preserve默认值: 如果 target 是
ES5,则为CommonJS;否则为ES6/ES2015。发布版本: 1.0
相关: moduleResolution,esModuleInterop,allowImportingTsExtensions,allowArbitraryExtensions,resolveJsonModule
设置程序的模块系统。有关更多信息,请参阅 TypeScript module 选项背后的理论及其参考页面。对于现代 Node.js 项目,你很可能需要 "nodenext";对于将被打包的代码,则可能需要 preserve 或 esnext。
更改 module 会影响 moduleResolution,它也有一个参考页面。
以下是此文件的一些示例输出:
// @filename: index.ts
import { valueOfPi } from "./constants";
export const twoPi = valueOfPi * 2;TryCommonJS
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.twoPi = void 0;
const constants_1 = require("./constants");
exports.twoPi = constants_1.valueOfPi * 2;
TryUMD
(function (factory) {
if (typeof module === "object" && typeof module.exports === "object") {
var v = factory(require, exports);
if (v !== undefined) module.exports = v;
}
else if (typeof define === "function" && define.amd) {
define(["require", "exports", "./constants"], factory);
}
})(function (require, exports) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.twoPi = void 0;
const constants_1 = require("./constants");
exports.twoPi = constants_1.valueOfPi * 2;
});
TryAMD
define(["require", "exports", "./constants"], function (require, exports, constants_1) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.twoPi = void 0;
exports.twoPi = constants_1.valueOfPi * 2;
});
TrySystem
System.register(["./constants"], function (exports_1, context_1) {
"use strict";
var constants_1, twoPi;
var __moduleName = context_1 && context_1.id;
return {
setters: [
function (constants_1_1) {
constants_1 = constants_1_1;
}
],
execute: function () {
exports_1("twoPi", twoPi = constants_1.valueOfPi * 2);
}
};
});
TryESNext
ES2015/ES6/ES2020/ES2022
除了 ES2015/ES6 的基本功能外,ES2020 增加了对动态 import 和 import.meta 的支持,而 ES2022 进一步增加了对顶层 await 的支持。
node16/node18/node20/nodenext
node16、node18、node20 和 nodenext 模式与 Node 的原生 ECMAScript 模块支持集成。输出的 JavaScript 根据文件扩展名和最近 package.json 中 type 设置的值使用 CommonJS 或 ES2020 输出。模块解析的工作方式也不同。你可以在手册和模块参考中了解更多信息。
node16从 TypeScript 4.7 开始可用node18从 TypeScript 5.8 开始可用,作为node16的替代,增加了对导入属性的支持。node20增加了对 require(ESM) 的支持。nodenext从 TypeScript 4.7 开始可用,但其行为随 Node.js 的最新稳定版本而变化。--module nodenext隐含浮动的--target esnext。
preserve
在 --module preserve(TypeScript 5.4 中添加)中,输入文件中编写的 ECMAScript 导入和导出在输出中保持不变,而 CommonJS 风格的 import x = require("...") 和 export = ... 语句则输出为 CommonJS require 和 module.exports。换句话说,每个单独的导入或导出语句的格式被保留,而不是被强制为整个编译(甚至整个文件)的单一格式。
import { valueOfPi } from "./constants";
const constants = require("./constants");
export const piSquared = valueOfPi * constants.valueOfPi;
Try虽然在同一文件中混合导入和 require 调用很少见,但此 module 模式最能反映大多数现代打包器以及 Bun 运行时的能力。
为什么要关心使用打包器或 Bun(你可能还会设置
noEmit)时的 TypeScriptmodule输出?TypeScript 的类型检查和模块解析行为受其将要输出的模块格式的影响。设置module可以让 TypeScript 了解你的打包器或运行时将如何处理导入和导出,这确保你在导入值上看到的类型准确反映运行时或打包后会发生的情况。
None
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.twoPi = void 0;
const constants_1 = require("./constants");
exports.twoPi = constants_1.valueOfPi * 2;
TrymoduleResolution
允许的值:
classic,node10/node,node16,nodenext,bundler默认值: 如果 module 是
CommonJS,则为Node10;如果 module 是Node16、Node18或Node20,则为Node16;如果 module 是NodeNext,则为NodeNext;如果 module 是Preserve,则为Bundler;否则为Classic。发布版本: 1.6
相关: module,paths,baseUrl,rootDirs,moduleSuffixes,customConditions,resolvePackageJsonExports,resolvePackageJsonImports
指定模块解析策略:
'node16'或'nodenext'用于现代版本的 Node.js。Node.js v12 及更高版本同时支持 ECMAScript 导入和 CommonJSrequire,它们使用不同的算法解析。这些moduleResolution值与相应的module值结合时,会根据 Node.js 在输出 JavaScript 代码中看到的是import还是require,为每个解析选择正确的算法。'node10'(以前称为'node')用于早于 v10 的 Node.js 版本,这些版本仅支持 CommonJSrequire。在现代代码中你可能不需要使用node10。'bundler'用于与打包器一起使用。与node16和nodenext一样,此模式支持 package.json"imports"和"exports",但与 Node.js 解析模式不同,bundler从不在导入中对相对路径要求文件扩展名。'classic'在 TypeScript 1.6 发布之前使用。不应使用classic。
有参考页面解释 TypeScript 模块解析背后的理论和每个选项的详细信息。
moduleSuffixes
- 发布版本: 4.7
提供一种覆盖默认文件名后缀列表的方法,以便在解析模块时搜索。
{
"compilerOptions": {
"moduleSuffixes": [".ios", ".native", ""]
}
}给定上述配置,如下所示的导入:
import * as foo from "./foo";TypeScript 将查找相对文件 ./foo.ios.ts、./foo.native.ts,最后是 ./foo.ts。
注意 moduleSuffixes 中的空字符串 "",这对于 TypeScript 也查找 ./foo.ts 是必要的。
此功能对于 React Native 项目很有用,其中每个目标平台可以使用具有不同 moduleSuffixes 的单独 tsconfig.json。
noResolve
- 发布版本: 1.0
默认情况下,TypeScript 将检查初始文件集以查找 import 和 <reference 指令,并将这些解析后的文件添加到你的程序中。
如果设置了 noResolve,则不会发生此过程。 但是,import 语句仍会被检查以查看它们是否解析为有效的模块,因此你需要通过其他方式确保满足此条件。
noUncheckedSideEffectImports
默认值:
true发布版本: 5.6
在 JavaScript 中,可以 import 一个模块而不实际从中导入任何值。
import "some-module";这些导入通常被称为副作用导入,因为它们能提供的唯一有用行为是通过执行某些副作用(如注册全局变量,或向原型添加 polyfill)。
默认情况下,TypeScript 不会检查这些导入的有效性。如果导入解析到有效的源文件,TypeScript 将加载并检查该文件。 如果找不到源文件,TypeScript 会静默忽略该导入。
这是令人惊讶的行为,但它部分源于对 JavaScript 生态系统中的模式进行建模。 例如,此语法也已与打包器中的特殊加载器一起使用,以加载 CSS 或其他资源。 你的打包器可能以某种方式配置,使得你可以通过编写如下代码来包含特定的 .css 文件:
import "./button-component.css";
export function Button() {
// ...
}尽管如此,这掩盖了副作用导入中潜在的拼写错误。
当启用 --noUncheckedSideEffectImports 时,如果 TypeScript 找不到副作用导入的源文件,现在将报错。
import "oops-this-module-does-not-exist";
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// 错误:找不到模块 'oops-this-module-does-not-exist' 或其对应的类型声明。启用此选项后,一些正常工作的代码现在可能会收到错误,就像上面的 CSS 示例一样。 为了解决这个问题,想要仅为资源编写副作用 import 的用户可能更适合编写带有通配符说明符的所谓环境模块声明。 它应该放在一个全局文件中,看起来像这样:
// ./src/globals.d.ts
// 将所有 CSS 文件识别为模块导入。
declare module "*.css" {}事实上,你的项目中可能已经有这样一个文件了! 例如,运行 vite init 可能会创建一个类似的 vite-env.d.ts。
paths
- 发布版本: 2.0
一系列条目,用于将导入重新映射到相对于 baseUrl(如果设置)或相对于 tsconfig 文件本身的查找位置。在 moduleResolution 参考页面中有更广泛的 paths 覆盖。
paths 允许你声明 TypeScript 应如何解析 require/import 中的导入。
{
"compilerOptions": {
"paths": {
"jquery": ["./vendor/jquery/dist/jquery"]
}
}
}这将允许你能够编写 import "jquery",并在本地获得所有正确的类型。
{
"compilerOptions": {
"paths": {
"app/*": ["./src/app/*"],
"config/*": ["./src/app/_config/*"],
"environment/*": ["./src/environments/*"],
"shared/*": ["./src/app/_shared/*"],
"helpers/*": ["./src/helpers/*"],
"tests/*": ["./src/tests/*"]
}
}
}在这种情况下,你可以告诉 TypeScript 文件解析器支持多个自定义前缀来查找代码。
请注意,此功能不会更改 tsc 输出导入路径的方式,因此 paths 应仅用于告知 TypeScript 另一个工具具有此映射,并将在运行时或打包时使用它。
resolveJsonModule
- 发布版本: 2.9
允许导入带有 .json 扩展名的模块,这是 node 项目中的常见做法。这包括根据静态 JSON 形状为 import 生成类型。
TypeScript 默认不支持解析 JSON 文件:
// @filename: settings.json
{
"repo": "TypeScript",
"dry": false,
"debug": false
}
// @filename: index.ts
import settings from "./settings.json";Cannot find module './settings.json'. Consider using '--resolveJsonModule' to import module with '.json' extension.
settings.debug === true;
settings.dry === 2;Try启用该选项允许导入 JSON,并验证该 JSON 文件中的类型。
// @filename: settings.json
{
"repo": "TypeScript",
"dry": false,
"debug": false
}
// @filename: index.ts
import settings from "./settings.json";
settings.debug === true;
settings.dry === 2;This comparison appears to be unintentional because the types 'boolean' and 'number' have no overlap.TryresolvePackageJsonExports
默认值: 当 moduleResolution 为
node16、nodenext或bundler时为true;否则为false发布版本: 5.0
相关: moduleResolution,customConditions,resolvePackageJsonImports
--resolvePackageJsonExports 强制 TypeScript 在从 node_modules 读取包时查阅 package.json 文件的 exports 字段。
在 --moduleResolution 的 node16、nodenext 和 bundler 选项下,此选项默认为 true。
resolvePackageJsonImports
默认值: 当 moduleResolution 为
node16、nodenext或bundler时为true;否则为false发布版本: 5.0
相关: moduleResolution,customConditions,resolvePackageJsonExports
--resolvePackageJsonImports 强制 TypeScript 在执行以 # 开头的查找时,如果文件的祖先目录包含 package.json,则查阅 package.json 文件的 imports 字段。
在 --moduleResolution 的 node16、nodenext 和 bundler 选项下,此选项默认为 true。
rewriteRelativeImportExtensions
- 发布版本: 5.7
在相对导入路径中将 .ts、.tsx、.mts 和 .cts 文件扩展名重写为输出文件中对应的 JavaScript 扩展名。
有关更多信息,请参阅 TypeScript 5.7 发布说明。
rootDir
默认值: 根据输入文件列表计算。
发布版本: 1.5
默认值:所有非声明输入文件的最长公共路径。如果设置了 composite,则默认值为包含 tsconfig.json 文件的目录。
当 TypeScript 编译文件时,它在输出目录中保持与输入目录相同的目录结构。
例如,假设你有一些输入文件:
MyProj
├── tsconfig.json
├── core
│ ├── a.ts
│ ├── b.ts
│ ├── sub
│ │ ├── c.ts
├── types.d.tsrootDir 的推断值是所有非声明输入文件的最长公共路径,在这种情况下是 core/。
如果你的 outDir 是 dist,TypeScript 将写入此树结构:
MyProj
├── dist
│ ├── a.js
│ ├── b.js
│ ├── sub
│ │ ├── c.js但是,你可能希望 core 成为输出目录结构的一部分。 通过在 tsconfig.json 中设置 rootDir: ".",TypeScript 将写入此树结构:
MyProj
├── dist
│ ├── core
│ │ ├── a.js
│ │ ├── b.js
│ │ ├── sub
│ │ │ ├── c.js重要的是,rootDir 不影响哪些文件成为编译的一部分。 它与 include、exclude 或 files tsconfig.json 设置没有交互。
请注意,TypeScript 永远不会将输出文件写入 outDir 之外的目录,并且永远不会跳过输出文件。 因此,rootDir 还强制所有需要输出的文件都在 rootDir 路径之下。
例如,假设你有此树结构:
MyProj
├── tsconfig.json
├── core
│ ├── a.ts
│ ├── b.ts
├── helpers.ts将 rootDir 指定为 core 并且 include 指定为 * 将是一个错误,因为它创建了一个需要输出到 outDir 之外(即 ../helpers.js)的文件(helpers.ts)。
rootDirs
默认值: 根据输入文件列表计算。
发布版本: 2.0
使用 rootDirs,你可以告知编译器有许多“虚拟”目录充当单个根。 这允许编译器解析这些“虚拟”目录中的相对模块导入,就像它们被合并到一个目录中一样。
例如:
src
└── views
└── view1.ts (可以导入 "./template1", "./view2`)
└── view2.ts (可以导入 "./template1", "./view1`)
generated
└── templates
└── views
└── template1.ts (可以导入 "./view1", "./view2"){
"compilerOptions": {
"rootDirs": ["src/views", "generated/templates/views"]
}
}这不会影响 TypeScript 输出 JavaScript 的方式,它只是模拟了它们在运行时能够通过这些相对路径工作的假设。
rootDirs 可用于为不是 TypeScript 或 JavaScript 的文件提供单独的“类型层”,方法是为生成的 .d.ts 文件在另一个文件夹中提供位置。此技术对于打包的应用程序很有用,在这些应用程序中,你使用 import 导入不一定代码的文件:
src
└── index.ts
└── css
└── main.css
└── navigation.css
generated
└── css
└── main.css.d.ts
└── navigation.css.d.ts{
"compilerOptions": {
"rootDirs": ["src", "generated"]
}
}此技术让你提前为非代码源文件生成类型。然后导入根据源文件的位置自然工作。 例如 ./src/index.ts 可以导入文件 ./src/css/main.css,TypeScript 将通过相应的生成声明文件了解打包器对该文件类型的行为。
typeRoots
默认情况下,所有可见的 "@types" 包都包含在你的编译中。 任何封闭文件夹中 node_modules/@types 中的包都被认为是可见的。 例如,这意味着 ./node_modules/@types/、../node_modules/@types/、../../node_modules/@types/ 等中的包。
如果指定了 typeRoots,则仅包含 typeRoots 下的包。例如:
{
"compilerOptions": {
"typeRoots": ["./typings", "./vendor/types"]
}
}此配置文件将包含 ./typings 和 ./vendor/types 下的所有包,而不包含 ./node_modules/@types 中的任何包。 所有路径都相对于 tsconfig.json。
types
默认情况下,所有可见的 "@types" 包都包含在你的编译中。 任何封闭文件夹中 node_modules/@types 中的包都被认为是可见的。 例如,这意味着 ./node_modules/@types/、../node_modules/@types/、../../node_modules/@types/ 等中的包。
如果指定了 types,则只有列出的包会包含在全局作用域中。例如:
{
"compilerOptions": {
"types": ["node", "jest", "express"]
}
}此 tsconfig.json 文件将仅包含 ./node_modules/@types/node、./node_modules/@types/jest 和 ./node_modules/@types/express。 node_modules/@types/* 下的其他包将不会被包含。
这会影响什么?
此选项不会影响 @types/* 如何包含在你的应用程序代码中,例如,如果你有上面的 compilerOptions 示例,代码如下:
import * as moment from "moment";
moment().format("MMMM Do YYYY, h:mm:ss a");moment 导入将完全被类型化。
当你设置了此选项时,不将模块包含在 types 数组中将会:
- 不会向你的项目添加全局变量(例如 node 中的
process,或 Jest 中的expect) - 不会将导出显示为自动导入建议
此功能与 typeRoots 的不同之处在于,它旨在指定你想要包含的确切类型,而 typeRoots 支持指定你想要特定文件夹。