From f2e31ebaff005e337de45509b4b5ffb81c7a5829 Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Sat, 17 Dec 2022 14:45:55 -0500 Subject: [PATCH 01/12] docs(website): blog post on consistent-type-(exports|imports) --- .../docs/rules/consistent-type-exports.md | 2 + .../docs/rules/consistent-type-imports.md | 2 + ...nt-type-exports-and-imports-why-and-how.md | 97 +++++++++++++++++++ 3 files changed, 101 insertions(+) create mode 100644 packages/website/blog/2022-12-23-consistent-type-exports-and-imports-why-and-how.md diff --git a/packages/eslint-plugin/docs/rules/consistent-type-exports.md b/packages/eslint-plugin/docs/rules/consistent-type-exports.md index ec15e9fdecb2..bc1a7dbc0aa4 100644 --- a/packages/eslint-plugin/docs/rules/consistent-type-exports.md +++ b/packages/eslint-plugin/docs/rules/consistent-type-exports.md @@ -9,6 +9,8 @@ description: 'Enforce consistent usage of type exports.' TypeScript allows specifying a `type` keyword on exports to indicate that the export exists only in the type system, not at runtime. This allows transpilers to drop exports without knowing the types of the dependencies. +> See [Blog > Consistent Type Exports and Imports: Why and How](/blog/consistent-type-exports-and-imports-why-and-how) for more details. + ## Examples diff --git a/packages/eslint-plugin/docs/rules/consistent-type-imports.md b/packages/eslint-plugin/docs/rules/consistent-type-imports.md index 045b7c2fa7da..d9accaf8c306 100644 --- a/packages/eslint-plugin/docs/rules/consistent-type-imports.md +++ b/packages/eslint-plugin/docs/rules/consistent-type-imports.md @@ -9,6 +9,8 @@ description: 'Enforce consistent usage of type imports.' TypeScript allows specifying a `type` keyword on imports to indicate that the export exists only in the type system, not at runtime. This allows transpilers to drop imports without knowing the types of the dependencies. +> See [Blog > Consistent Type Exports and Imports: Why and How](/blog/consistent-type-exports-and-imports-why-and-how) for more details. + ## Options ### `prefer` diff --git a/packages/website/blog/2022-12-23-consistent-type-exports-and-imports-why-and-how.md b/packages/website/blog/2022-12-23-consistent-type-exports-and-imports-why-and-how.md new file mode 100644 index 000000000000..469bdd3eecba --- /dev/null +++ b/packages/website/blog/2022-12-23-consistent-type-exports-and-imports-why-and-how.md @@ -0,0 +1,97 @@ +--- +authors: + - image_url: https://www.joshuakgoldberg.com/img/josh.jpg + name: Josh Goldberg + title: typescript-eslint Maintainer + url: https://github.com/JoshuaKGoldberg +description: Why enforcing TypeScript imports use the `type` modifier when possible benefits some project setups. +slug: consistent-type-exports-and-imports-why-and-how +tags: [typescript, imports, exports, types, transpiling] +title: 'Consistent Type Exports and Imports: Why and How' +--- + +TypeScript 3.8 [added type-only imports and exports](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-8.html): + +```ts +import type { SomeThing } from './some-module.js'; +export type { SomeThing }; +``` + +The key difference with `export type` and `import type` is that they are _only in the type system_. +Attempting to use a _value_ imported as only a _type_ in runtime code will cause a TypeScript error: + +```ts twoslash +import type { SomeThing } from './some-module.js'; + +new SomeThing(); +// ~~~~~~~~~ +// 'SomeThing' cannot be used as a value +// because it was imported using 'import type'. +``` + +This brings up two questions: + +- Why would you want to use these type-only imports and exports? +- How can you enforce a project use them whenever necessary? + +Let's Dig In! + + + +## Functional Benefits + +Some modules in code may cause _side effects_: code that is run when the module is imported and causes changes outside the module. +Common examples of side effects include calling to global APIs like `fetch` or creating DOM stylesheets. + +When projects include modules that cause side effects, the order of module imports matters. +For example, some projects import the types of side-effect-causing modules in code that needs to run before those side effects. + +Import statements that only import types are generally removed when the built-in TypeScript compiler transpiles TypeScript syntax to JavaScript syntax. +The built-in TypeScript compiler is able to do so because it includes a type checker that knows which imports are of types and/or values. + +But, some projects use transpilers such as Babel and Vite that don't have access to type information. +Those untyped transpilers can't know whether an import is of a type, a value, or both: + +```ts +// Is SomeThing a class? A type? A variable? +// Just from this file, we don't know! 😫 +export { SomeThing } from './may-include-side-effects.js'; +``` + +If that `./may-include-side-effects.js` module includes side effects, keeping or removing the import can have very different runtime behaviors in the project. +Indicating in code which values are type-only can be necessary for transpilers that don't have access to TypeScript's type system to know whether to keep or remove the import. + +```ts +// Now we know this file's SomeThing is only used as a type. +// We can remove this export in transpiled JavaScript syntax. +export type { SomeThing } from './may-include-side-effects.js'; +``` + +## Enforcing With typescript-eslint + +typescript-eslint provides two ESLint rules that can standardize using (or not using) type-only exports and imports: + +- [`@typescript-eslint/consistent-type-exports`](https://typescript-eslint.io/rules/consistent-type-exports): Enforce consistent usage of type exports. +- [`@typescript-eslint/consistent-type-imports`](https://typescript-eslint.io/rules/consistent-type-imports): Enforce consistent usage of type imports. + +You can enable them in your [ESLint configuration](https://eslint.org/docs/latest/user-guide/configuring): + +```json +{ + "rules": { + "@typescript-eslint/consistent-type-exports": "error", + "@typescript-eslint/consistent-type-imports": "warn" + } +} +``` + +The two rules can auto-fix code to use `type`s as necessary when ESLint is run on the command-line with `--fix` or configured in an editor extension such as the [VSCode ESLint extension](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint). + +You can read more about the rules' configuration options in their docs pages. +See [our Getting Started docs](https://typescript-eslint.io/getting-started) for more information on linting your TypeScript code with typescript-eslint. + +## Supporting typescript-eslint + +If you enjoyed this blog post and/or or use typescript-eslint, please consider [supporting us on Open Collective](https://opencollective.com/typescript-eslint). +We're a small volunteer team and could use your support to make the ESLint experience on TypeScript great. +Thanks! 💖 From c1c5db2989bfa3a2651bf96412db33970ad64830 Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Sat, 4 Feb 2023 21:03:53 -0500 Subject: [PATCH 02/12] Apply suggestions from code review Co-authored-by: Brad Zacher --- ...12-23-consistent-type-exports-and-imports-why-and-how.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/website/blog/2022-12-23-consistent-type-exports-and-imports-why-and-how.md b/packages/website/blog/2022-12-23-consistent-type-exports-and-imports-why-and-how.md index 469bdd3eecba..cc385a94176e 100644 --- a/packages/website/blog/2022-12-23-consistent-type-exports-and-imports-why-and-how.md +++ b/packages/website/blog/2022-12-23-consistent-type-exports-and-imports-why-and-how.md @@ -40,16 +40,20 @@ Let's Dig In! ## Functional Benefits +### Module Side Effects + Some modules in code may cause _side effects_: code that is run when the module is imported and causes changes outside the module. Common examples of side effects include calling to global APIs like `fetch` or creating DOM stylesheets. When projects include modules that cause side effects, the order of module imports matters. For example, some projects import the types of side-effect-causing modules in code that needs to run before those side effects. +### Isolated Module Transpilation + Import statements that only import types are generally removed when the built-in TypeScript compiler transpiles TypeScript syntax to JavaScript syntax. The built-in TypeScript compiler is able to do so because it includes a type checker that knows which imports are of types and/or values. -But, some projects use transpilers such as Babel and Vite that don't have access to type information. +But, some projects use transpilers such as Babel, SWC or Vite that don't have access to type information. Those untyped transpilers can't know whether an import is of a type, a value, or both: ```ts From 8403dddeb26284bb20f39fcf5a6dddc147a2cdae Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Sat, 4 Feb 2023 21:04:09 -0500 Subject: [PATCH 03/12] Apply suggestions from code review Co-authored-by: Brad Zacher --- ...22-12-23-consistent-type-exports-and-imports-why-and-how.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/website/blog/2022-12-23-consistent-type-exports-and-imports-why-and-how.md b/packages/website/blog/2022-12-23-consistent-type-exports-and-imports-why-and-how.md index cc385a94176e..266a1d51506f 100644 --- a/packages/website/blog/2022-12-23-consistent-type-exports-and-imports-why-and-how.md +++ b/packages/website/blog/2022-12-23-consistent-type-exports-and-imports-why-and-how.md @@ -17,7 +17,7 @@ import type { SomeThing } from './some-module.js'; export type { SomeThing }; ``` -The key difference with `export type` and `import type` is that they are _only in the type system_. +The key difference with `export type` and `import type` is that they _do not represent runtime code_. Attempting to use a _value_ imported as only a _type_ in runtime code will cause a TypeScript error: ```ts twoslash @@ -29,6 +29,7 @@ new SomeThing(); // because it was imported using 'import type'. ``` +This is because type-only imports and exports are not emitted as runtime code when code is transpiled to JavaScript. This brings up two questions: - Why would you want to use these type-only imports and exports? From 5b6f938e4f75fc70b7677ec3c1a0132ab9ac91c3 Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Sat, 4 Feb 2023 21:04:39 -0500 Subject: [PATCH 04/12] More review touchups --- ...ent-type-exports-and-imports-why-and-how.md | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/packages/website/blog/2022-12-23-consistent-type-exports-and-imports-why-and-how.md b/packages/website/blog/2022-12-23-consistent-type-exports-and-imports-why-and-how.md index 266a1d51506f..f5be3a9a3449 100644 --- a/packages/website/blog/2022-12-23-consistent-type-exports-and-imports-why-and-how.md +++ b/packages/website/blog/2022-12-23-consistent-type-exports-and-imports-why-and-how.md @@ -10,7 +10,7 @@ tags: [typescript, imports, exports, types, transpiling] title: 'Consistent Type Exports and Imports: Why and How' --- -TypeScript 3.8 [added type-only imports and exports](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-8.html): +TypeScript 3.8 [added type-only imports and exports](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-8.html#type-only-imports-and-export) to the language: ```ts import type { SomeThing } from './some-module.js'; @@ -76,8 +76,8 @@ export type { SomeThing } from './may-include-side-effects.js'; typescript-eslint provides two ESLint rules that can standardize using (or not using) type-only exports and imports: -- [`@typescript-eslint/consistent-type-exports`](https://typescript-eslint.io/rules/consistent-type-exports): Enforce consistent usage of type exports. -- [`@typescript-eslint/consistent-type-imports`](https://typescript-eslint.io/rules/consistent-type-imports): Enforce consistent usage of type imports. +- [`@typescript-eslint/consistent-type-exports`](/rules/consistent-type-exports): Enforce consistent usage of type exports. +- [`@typescript-eslint/consistent-type-imports`](/rules/consistent-type-imports): Enforce consistent usage of type imports. You can enable them in your [ESLint configuration](https://eslint.org/docs/latest/user-guide/configuring): @@ -85,15 +85,23 @@ You can enable them in your [ESLint configuration](https://eslint.org/docs/lates { "rules": { "@typescript-eslint/consistent-type-exports": "error", - "@typescript-eslint/consistent-type-imports": "warn" + "@typescript-eslint/consistent-type-imports": "error" } } ``` The two rules can auto-fix code to use `type`s as necessary when ESLint is run on the command-line with `--fix` or configured in an editor extension such as the [VSCode ESLint extension](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint). +## Further Reading + You can read more about the rules' configuration options in their docs pages. -See [our Getting Started docs](https://typescript-eslint.io/getting-started) for more information on linting your TypeScript code with typescript-eslint. +See [our Getting Started docs](/getting-started) for more information on linting your TypeScript code with typescript-eslint. + +TypeScript 4.5 also added [inline type qualifiers](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-4-5.html#type-modifiers-on-import-names), which allow for specifying type-only imports: + +```ts +import { type SomeType, SomeValue } from './some-module.js'; +``` ## Supporting typescript-eslint From 74482dd4d7d5f145fc135f905279e8b0c389db76 Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Sat, 4 Feb 2023 21:12:32 -0500 Subject: [PATCH 05/12] More informative examples --- ...nt-type-exports-and-imports-why-and-how.md | 29 +++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/packages/website/blog/2022-12-23-consistent-type-exports-and-imports-why-and-how.md b/packages/website/blog/2022-12-23-consistent-type-exports-and-imports-why-and-how.md index f5be3a9a3449..6701ad1e47ae 100644 --- a/packages/website/blog/2022-12-23-consistent-type-exports-and-imports-why-and-how.md +++ b/packages/website/blog/2022-12-23-consistent-type-exports-and-imports-why-and-how.md @@ -54,8 +54,11 @@ For example, some projects import the types of side-effect-causing modules in co Import statements that only import types are generally removed when the built-in TypeScript compiler transpiles TypeScript syntax to JavaScript syntax. The built-in TypeScript compiler is able to do so because it includes a type checker that knows which imports are of types and/or values. -But, some projects use transpilers such as Babel, SWC or Vite that don't have access to type information. -Those untyped transpilers can't know whether an import is of a type, a value, or both: +But, some projects use transpilers such as Babel, SWC, or Vite that don't have access to type information. +These transpilers are sometimes referred to as _isolated module transpilers_ because they effectively transpile each module in isolation from other modules. +Isolated module transpilers can't know whether an import is of a type, a value, or both. + +Take this file with exactly three lines of code: ```ts // Is SomeThing a class? A type? A variable? @@ -83,6 +86,7 @@ You can enable them in your [ESLint configuration](https://eslint.org/docs/lates ```json { + "plugins": ["@typescript-eslint"], "rules": { "@typescript-eslint/consistent-type-exports": "error", "@typescript-eslint/consistent-type-imports": "error" @@ -90,8 +94,29 @@ You can enable them in your [ESLint configuration](https://eslint.org/docs/lates } ``` +With those rules enabled, running ESLint on the following code would produce a lint complaint: + +```ts +import { GetString } from './types.js'; +// All imports in the declaration are only used as types. Use `import type`. + +export function getAndLogValue(getter: GetString) { + console.log('Value:', getter()); +} +``` + The two rules can auto-fix code to use `type`s as necessary when ESLint is run on the command-line with `--fix` or configured in an editor extension such as the [VSCode ESLint extension](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint). +For example, the `import` statement from earlier would be autofixed to: + +```ts +import type { GetString } from './types.js'; + +export function getAndLogValue(getter: GetString) { + console.log('Value:', getter()); +} +``` + ## Further Reading You can read more about the rules' configuration options in their docs pages. From 9026354158fc4bf16687c09135c6e17ba445ec4f Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Sat, 4 Feb 2023 21:13:36 -0500 Subject: [PATCH 06/12] Rename and redate --- ...-02-06-consistent-type-exports-and-imports-why-and-how.md} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename packages/website/blog/{2022-12-23-consistent-type-exports-and-imports-why-and-how.md => 2023-02-06-consistent-type-exports-and-imports-why-and-how.md} (98%) diff --git a/packages/website/blog/2022-12-23-consistent-type-exports-and-imports-why-and-how.md b/packages/website/blog/2023-02-06-consistent-type-exports-and-imports-why-and-how.md similarity index 98% rename from packages/website/blog/2022-12-23-consistent-type-exports-and-imports-why-and-how.md rename to packages/website/blog/2023-02-06-consistent-type-exports-and-imports-why-and-how.md index 6701ad1e47ae..d18aad261878 100644 --- a/packages/website/blog/2022-12-23-consistent-type-exports-and-imports-why-and-how.md +++ b/packages/website/blog/2023-02-06-consistent-type-exports-and-imports-why-and-how.md @@ -5,9 +5,9 @@ authors: title: typescript-eslint Maintainer url: https://github.com/JoshuaKGoldberg description: Why enforcing TypeScript imports use the `type` modifier when possible benefits some project setups. -slug: consistent-type-exports-and-imports-why-and-how +slug: consistent-type-imports-and-exports-why-and-how tags: [typescript, imports, exports, types, transpiling] -title: 'Consistent Type Exports and Imports: Why and How' +title: 'Consistent Type Imports and Exports: Why and How' --- TypeScript 3.8 [added type-only imports and exports](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-8.html#type-only-imports-and-export) to the language: From 98c074143acec3e30aa109b6b8057e56466d96d0 Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Sat, 4 Feb 2023 21:17:04 -0500 Subject: [PATCH 07/12] Fix build links --- packages/eslint-plugin/docs/rules/consistent-type-exports.md | 2 +- packages/eslint-plugin/docs/rules/consistent-type-imports.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/eslint-plugin/docs/rules/consistent-type-exports.md b/packages/eslint-plugin/docs/rules/consistent-type-exports.md index bc1a7dbc0aa4..57682fc176d4 100644 --- a/packages/eslint-plugin/docs/rules/consistent-type-exports.md +++ b/packages/eslint-plugin/docs/rules/consistent-type-exports.md @@ -9,7 +9,7 @@ description: 'Enforce consistent usage of type exports.' TypeScript allows specifying a `type` keyword on exports to indicate that the export exists only in the type system, not at runtime. This allows transpilers to drop exports without knowing the types of the dependencies. -> See [Blog > Consistent Type Exports and Imports: Why and How](/blog/consistent-type-exports-and-imports-why-and-how) for more details. +> See [Blog > Consistent Type Exports and Imports: Why and How](/blog/consistent-type-imports-and-exports-why-and-how) for more details. ## Examples diff --git a/packages/eslint-plugin/docs/rules/consistent-type-imports.md b/packages/eslint-plugin/docs/rules/consistent-type-imports.md index d9accaf8c306..3ca8920fb4b7 100644 --- a/packages/eslint-plugin/docs/rules/consistent-type-imports.md +++ b/packages/eslint-plugin/docs/rules/consistent-type-imports.md @@ -9,7 +9,7 @@ description: 'Enforce consistent usage of type imports.' TypeScript allows specifying a `type` keyword on imports to indicate that the export exists only in the type system, not at runtime. This allows transpilers to drop imports without knowing the types of the dependencies. -> See [Blog > Consistent Type Exports and Imports: Why and How](/blog/consistent-type-exports-and-imports-why-and-how) for more details. +> See [Blog > Consistent Type Exports and Imports: Why and How](/blog/consistent-type-imports-and-exports-why-and-how) for more details. ## Options From 9b845bafe77c38d039b0f3cd8232f15878457133 Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Sat, 4 Feb 2023 21:41:12 -0500 Subject: [PATCH 08/12] Spelling --- ...023-02-06-consistent-type-exports-and-imports-why-and-how.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/website/blog/2023-02-06-consistent-type-exports-and-imports-why-and-how.md b/packages/website/blog/2023-02-06-consistent-type-exports-and-imports-why-and-how.md index d18aad261878..8b32c904fa92 100644 --- a/packages/website/blog/2023-02-06-consistent-type-exports-and-imports-why-and-how.md +++ b/packages/website/blog/2023-02-06-consistent-type-exports-and-imports-why-and-how.md @@ -107,7 +107,7 @@ export function getAndLogValue(getter: GetString) { The two rules can auto-fix code to use `type`s as necessary when ESLint is run on the command-line with `--fix` or configured in an editor extension such as the [VSCode ESLint extension](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint). -For example, the `import` statement from earlier would be autofixed to: +For example, the `import` statement from earlier would be auto-fixed to: ```ts import type { GetString } from './types.js'; From fba73d9245ce8703f946cbfd3cf5f07ac38f0faf Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Sun, 5 Feb 2023 01:30:25 -0500 Subject: [PATCH 09/12] Apply suggestions from code review Co-authored-by: Joshua Chen --- ...-06-consistent-type-exports-and-imports-why-and-how.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/website/blog/2023-02-06-consistent-type-exports-and-imports-why-and-how.md b/packages/website/blog/2023-02-06-consistent-type-exports-and-imports-why-and-how.md index 8b32c904fa92..040016290b9a 100644 --- a/packages/website/blog/2023-02-06-consistent-type-exports-and-imports-why-and-how.md +++ b/packages/website/blog/2023-02-06-consistent-type-exports-and-imports-why-and-how.md @@ -39,19 +39,19 @@ Let's Dig In! -## Functional Benefits +## Benefits of Enforcing Type-only Imports/Exports -### Module Side Effects +### Avoiding Unintentional Side Effects Some modules in code may cause _side effects_: code that is run when the module is imported and causes changes outside the module. -Common examples of side effects include calling to global APIs like `fetch` or creating DOM stylesheets. +Common examples of side effects include sending network requests via `fetch` or creating DOM stylesheets. When projects include modules that cause side effects, the order of module imports matters. For example, some projects import the types of side-effect-causing modules in code that needs to run before those side effects. ### Isolated Module Transpilation -Import statements that only import types are generally removed when the built-in TypeScript compiler transpiles TypeScript syntax to JavaScript syntax. +Import statements that only import types are generally removed when the TypeScript compiler transpiles TypeScript syntax to JavaScript syntax. The built-in TypeScript compiler is able to do so because it includes a type checker that knows which imports are of types and/or values. But, some projects use transpilers such as Babel, SWC, or Vite that don't have access to type information. From 6ebebd33fb12da88959f691e8ba6113c26263d12 Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Sun, 5 Feb 2023 01:34:11 -0500 Subject: [PATCH 10/12] Switch to import --- ...02-06-consistent-type-exports-and-imports-why-and-how.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/website/blog/2023-02-06-consistent-type-exports-and-imports-why-and-how.md b/packages/website/blog/2023-02-06-consistent-type-exports-and-imports-why-and-how.md index 040016290b9a..f50bc0efdd02 100644 --- a/packages/website/blog/2023-02-06-consistent-type-exports-and-imports-why-and-how.md +++ b/packages/website/blog/2023-02-06-consistent-type-exports-and-imports-why-and-how.md @@ -63,7 +63,7 @@ Take this file with exactly three lines of code: ```ts // Is SomeThing a class? A type? A variable? // Just from this file, we don't know! 😫 -export { SomeThing } from './may-include-side-effects.js'; +import { SomeThing } from './may-include-side-effects.js'; ``` If that `./may-include-side-effects.js` module includes side effects, keeping or removing the import can have very different runtime behaviors in the project. @@ -71,8 +71,8 @@ Indicating in code which values are type-only can be necessary for transpilers t ```ts // Now we know this file's SomeThing is only used as a type. -// We can remove this export in transpiled JavaScript syntax. -export type { SomeThing } from './may-include-side-effects.js'; +// We can remove this import in transpiled JavaScript syntax. +import type { SomeThing } from './may-include-side-effects.js'; ``` ## Enforcing With typescript-eslint From 729853a2fd12fb874660df43295ec6cc53c93a70 Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Tue, 21 Feb 2023 15:42:42 -0500 Subject: [PATCH 11/12] Mention eslint-plugin-import and verbatimModuleSyntax --- ...t-type-exports-and-imports-why-and-how.md} | 70 ++++++++++++++++--- 1 file changed, 62 insertions(+), 8 deletions(-) rename packages/website/blog/{2023-02-06-consistent-type-exports-and-imports-why-and-how.md => 2023-02-24-consistent-type-exports-and-imports-why-and-how.md} (58%) diff --git a/packages/website/blog/2023-02-06-consistent-type-exports-and-imports-why-and-how.md b/packages/website/blog/2023-02-24-consistent-type-exports-and-imports-why-and-how.md similarity index 58% rename from packages/website/blog/2023-02-06-consistent-type-exports-and-imports-why-and-how.md rename to packages/website/blog/2023-02-24-consistent-type-exports-and-imports-why-and-how.md index f50bc0efdd02..c283d8055a62 100644 --- a/packages/website/blog/2023-02-06-consistent-type-exports-and-imports-why-and-how.md +++ b/packages/website/blog/2023-02-24-consistent-type-exports-and-imports-why-and-how.md @@ -10,7 +10,13 @@ tags: [typescript, imports, exports, types, transpiling] title: 'Consistent Type Imports and Exports: Why and How' --- -TypeScript 3.8 [added type-only imports and exports](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-8.html#type-only-imports-and-export) to the language: +`import` and `export` statements are core features of the JavaScript language. +They were added as part of the [ECMAScript Modules (ESM)](https://nodejs.org/api/esm.html#modules-ecmascript-modules) specification, and now are generally available in most mainstream runtimes, including all evergreen browsers and Node.js. + +When writing TypeScript code with ESM, it can sometimes be desirable to import or export a type only in the type system. +Code may wish to refer to a _type_, but not actually import or export a corresponding _value_. + +For that purpose, TypeScript 3.8 [added type-only imports and exports](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-8.html#type-only-imports-and-export) to the TypeScript language: ```ts import type { SomeThing } from './some-module.js'; @@ -29,7 +35,13 @@ new SomeThing(); // because it was imported using 'import type'. ``` -This is because type-only imports and exports are not emitted as runtime code when code is transpiled to JavaScript. +TypeScript 4.5 also added [inline type qualifiers](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-4-5.html#type-modifiers-on-import-names), which allow for indicating that only some specifiers in a statement should be type-system-only: + +```ts +import { type SomeType, SomeValue } from './some-module.js'; +``` + +Type-only imports and exports are not emitted as runtime code when code is transpiled to JavaScript. This brings up two questions: - Why would you want to use these type-only imports and exports? @@ -117,17 +129,59 @@ export function getAndLogValue(getter: GetString) { } ``` -## Further Reading +## More Lint Rules -You can read more about the rules' configuration options in their docs pages. -See [our Getting Started docs](/getting-started) for more information on linting your TypeScript code with typescript-eslint. +### `import` Plugin Rules -TypeScript 4.5 also added [inline type qualifiers](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-4-5.html#type-modifiers-on-import-names), which allow for specifying type-only imports: +[`eslint-plugin-import`](https://github.com/import-js/eslint-plugin-import) is a handy plugin with rules that validate proper imports. +Although some of those rules are made redundant by TypeScript, many are still relevant for TypeScript code. -```ts -import { type SomeType, SomeValue } from './some-module.js'; +Two of those rules in particular can be helpful for consistent `type` imports: + +- [`import/consistent-type-specifier-style`](https://github.com/import-js/eslint-plugin-import/blob/main/docs/rules/consistent-type-specifier-style.md): enforces consistent use of top-level vs inline `type` qualifier +- [`import/no-duplicates`](https://github.com/import-js/eslint-plugin-import/blob/main/docs/rules/no-duplicates.md#inline-type-imports): warns against unnecessary duplicate imports (and with the `inline-type-imports` option can work in tandem with `import/consistent-type-specifier-style`). + +In conjunction with [`@typescript-eslint/consistent-type-imports`](/rules/consistent-type-imports), [`eslint-plugin-import`](https://github.com/import-js/eslint-plugin-import)'s rules can enforce your imports are always properly qualified and are written in a standard, predictable style (eg always top-level type qualifier or always inline type-qualifier). + +### Verbatim Module Syntax + +TypeScript 5.0 additionally adds a new [`--verbatimModuleSyntax`](https://devblogs.microsoft.com/typescript/announcing-typescript-5-0-beta/#verbatimmodulesyntax) compiler option. +`verbatimModuleSyntax` simplifies TypeScript's logic around whether to preserve imports. +From the TypeScript release notes: + +> ...any imports or exports without a type modifier are left around. +> Anything that uses the type modifier is dropped entirely. +> +> ```ts +> // Erased away entirely. +> import type { A } from 'a'; +> +> // Rewritten to 'import { b } from 'bcd';' +> import { b, type c, type d } from 'bcd'; +> +> // Rewritten to 'import {} from 'xyz';' +> import { type xyz } from 'xyz'; +> ``` +> +> With this new option, what you see is what you get. + +`verbatimModuleSyntax` is useful for simplifying transpilation logic around imports - though it does mean that transpiled code such as the may end up with unnecessary import statements. +The `import { type xyz } from 'xyz';` line from the previous code snippet is an example of this. +For the rare case of needing to import for side effects, leaving in those statements may be desirable - but for most cases you will not want to leave behind an unnecessary side effect import. + +typescript-eslint now provides a [`@typescript-eslint/no-import-type-side-effects`](/rules/no-import-type-side-effects) rule to flag those cases. +If it detects an import that only imports specifiers with inline `type` qualifiers, it will suggest rewriting the import to use a top-level `type` qualifier: + +```diff +- import { type A } from 'xyz'; ++ import type { A } from 'xyz'; ``` +## Further Reading + +You can read more about the rules' configuration options in their docs pages. +See [our Getting Started docs](/getting-started) for more information on linting your TypeScript code with typescript-eslint. + ## Supporting typescript-eslint If you enjoyed this blog post and/or or use typescript-eslint, please consider [supporting us on Open Collective](https://opencollective.com/typescript-eslint). From f39b1d503b19dab47f69b25322c2ffaddb8cb4e4 Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Tue, 21 Feb 2023 16:02:14 -0500 Subject: [PATCH 12/12] Fix spelling --- ...023-02-24-consistent-type-exports-and-imports-why-and-how.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/website/blog/2023-02-24-consistent-type-exports-and-imports-why-and-how.md b/packages/website/blog/2023-02-24-consistent-type-exports-and-imports-why-and-how.md index c283d8055a62..2135f6e3b3b5 100644 --- a/packages/website/blog/2023-02-24-consistent-type-exports-and-imports-why-and-how.md +++ b/packages/website/blog/2023-02-24-consistent-type-exports-and-imports-why-and-how.md @@ -11,7 +11,7 @@ title: 'Consistent Type Imports and Exports: Why and How' --- `import` and `export` statements are core features of the JavaScript language. -They were added as part of the [ECMAScript Modules (ESM)](https://nodejs.org/api/esm.html#modules-ecmascript-modules) specification, and now are generally available in most mainstream runtimes, including all evergreen browsers and Node.js. +They were added as part of the [ECMAScript Modules (ESM)](https://nodejs.org/api/esm.html#modules-ecmascript-modules) specification, and now are generally available in most mainstream JavaScript environments, including all evergreen browsers and Node.js. When writing TypeScript code with ESM, it can sometimes be desirable to import or export a type only in the type system. Code may wish to refer to a _type_, but not actually import or export a corresponding _value_.