You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
@@ -72,10 +72,11 @@ Based on your answers to the above questions, here's how the complete `tsconfig.
72
72
"strict": true,
73
73
"noUncheckedIndexedAccess": true,
74
74
75
-
/* If transpiling with TypeScript: */
75
+
/* If transpiling with tsc: */
76
76
"module": "NodeNext",
77
77
"outDir": "dist",
78
78
"sourceMap": true,
79
+
"verbatimModuleSyntax": true,
79
80
80
81
/* AND if you're building for a library: */
81
82
"declaration": true,
@@ -84,7 +85,7 @@ Based on your answers to the above questions, here's how the complete `tsconfig.
84
85
"composite": true,
85
86
"declarationMap": true,
86
87
87
-
/* If NOT transpiling with TypeScript: */
88
+
/* If NOT transpiling with tsc: */
88
89
"module": "Preserve",
89
90
"noEmit": true,
90
91
@@ -439,27 +440,139 @@ TypeScript has gone through various iterations of configuration options to suppo
439
440
440
441
The behavior described above, where imports are elided if they're only used for types, is what happens when `verbatimModuleSyntax` is set to `true`.
441
442
442
-
You might be wondering why it isn't part of the recommended set of options detailed above. The reason is that it also has some impact on how modules work in TypeScript - we'll talk about that in the next section.
443
-
444
443
## ESM and CommonJS
445
444
446
-
<!-- TODO -->
445
+
There are two ways to modularize your code in TypeScript: ECMAScript Modules (ESM) and CommonJS (CJS). These two module systems operate slightly differently, and they don't always work together cleanly.
446
+
447
+
ES Modules use `import` and `export` statements:
448
+
449
+
```typescript
450
+
import { createAlbum } from"./album";
451
+
452
+
export { createAlbum };
453
+
```
454
+
455
+
CommonJS uses `require` and `module.exports`:
456
+
457
+
```typescript
458
+
const { createAlbum } =require("./album");
459
+
460
+
module.exports= { createAlbum };
461
+
```
462
+
463
+
Understanding the interoperability issues between ESM and CJS is a little beyond the scope of this book. Instead, we'll look at how to set up TypeScript to make working with both module systems as easy as possible.
464
+
465
+
### How Does TypeScript Know What Module System To Emit?
466
+
467
+
Imagine we have our `album.ts` file that exports a `createAlbum` function:
468
+
469
+
```typescript
470
+
// album.ts
471
+
472
+
exportfunction createAlbum(
473
+
title:string,
474
+
artist:string,
475
+
year:number,
476
+
):Album {
477
+
return { title, artist, year };
478
+
}
479
+
```
480
+
481
+
When this file is turned into JavaScript, should it emit `CJS` or `ESM` syntax?
482
+
483
+
```javascript
484
+
// ESM
485
+
486
+
exportfunctioncreateAlbum(title, artist, year) {
487
+
return { title, artist, year };
488
+
}
489
+
```
490
+
491
+
```javascript
492
+
// CJS
493
+
494
+
functioncreateAlbum(title, artist, year) {
495
+
return { title, artist, year };
496
+
}
497
+
498
+
module.exports= {
499
+
createAlbum,
500
+
};
501
+
```
502
+
503
+
The way this is decided is via `module`. You can hardcode this by choosing some older options. `module: CommonJS` will always emit CommonJS syntax, and `module: ESNext` will always emit ESM syntax.
504
+
505
+
But if you're using TypeScript to transpile your code, I recommend using `module: NodeNext`. This has several complex rules built-in for understanding whether to emit CJS or ESM:
506
+
507
+
The first way we can influence how TypeScript emits your modules with `module: NodeNext` is by using `.cts` and `.mts` extensions.
447
508
448
-
### `type` In `package.json` And `module: NodeNext`
509
+
If we change `album.ts` to `album.cts`, TypeScript will emit CommonJS syntax, and the emitted file extension will be `.cjs`.
449
510
450
-
<!-- TODO -->
511
+
If we change `album.ts` to `album.mts`, TypeScript will emit ESM syntax, and the emitted file extension will be `.mjs`.
512
+
513
+
If we keep `album.ts` the same, TypeScript will look up the directories for the closest `package.json` file. If the `type` field is set to `module`, TypeScript will emit ESM syntax. If it's set to `commonjs` (or unset, matching Node's behavior), TypeScript will emit CJS syntax.
|`album.ts`|`album.js`| Depends on `type` in `package.json`|
451
520
452
521
### `verbatimModuleSyntax` With ESM and CommonJS
453
522
454
-
<!-- TODO -->
523
+
`verbatimModuleSyntax` can help you be more explicit about which module system you're using. If you set `verbatimModuleSyntax` to `true`, TypeScript will error if you try to use `require` in an ESM file, or `import` in a CJS file.
524
+
525
+
For example, consider this file `hello.cts` that uses the `export default` syntax:
526
+
527
+
```tsx
528
+
// hello.cts
529
+
const hello = () => {
530
+
console.log("Hello!");
531
+
};
532
+
533
+
export { hello }; // red squiggly line under export { hello }
534
+
```
535
+
536
+
When `verbatimModuleSyntax` is enabled, TypeScript will show an error under the `export default` line that tells us we're mixing the syntaxes together:
In order to fix the issue, we need to use the `export =` syntax instead:
544
+
545
+
```tsx
546
+
// hello.cts
547
+
548
+
const hello = () => {
549
+
console.log("Hello!");
550
+
};
551
+
export= { hello };
552
+
```
553
+
554
+
This will compile down to `module.exports = { hello }` in the emitted JavaScript.
457
555
458
-
`import X = require('x')`
556
+
The warnings will show when trying to use an ESM import as well:
557
+
558
+
```tsx
559
+
import { z } from"zod"; // rsl under import statement
560
+
561
+
// hovering over the import shows:
562
+
// ESM syntax is not allowed in a CommonJS module when 'verbatimModuleSyntax' is enabled.
563
+
```
564
+
565
+
Here, the fix is to use `require` instead of `import`:
566
+
567
+
```tsx
568
+
importzod=require("zod");
569
+
570
+
const z =zod.z;
571
+
```
459
572
460
-
<!-- TODO -->
573
+
Note that this syntax combines `import` and `require` in a curious way - this is a TypeScript-specific syntax that gives you autocomplete in CommonJS modules.
461
574
462
-
#### When `verbatimModuleSyntax`Isn't Appropriate
575
+
`verbatimModuleSyntax`is a great way to catch these issues early, and to make sure you're using the right module system in the correct files. It pairs very well with `module: NodeNext`.
0 commit comments