verbatimModuleSyntax în TypeScript

Importurile rămân exact cum sunt scrise; se șterg doar cele marcate cu type, deci tipurile trebuie importate cu import type.

Sintaxă
"compilerOptions": { "verbatimModuleSyntax": true }

Ce face verbatimModuleSyntax

Fără opțiune, tsc șterge din JavaScript importurile folosite doar ca tipuri și pe cele nefolosite. De obicei e bine, dar uneori un import trebuie păstrat pentru efectele lui (codul care rulează la încărcarea modulului), iar alte unelte nu pot ști ce să șteargă fără să citească celelalte fișiere.

Cu verbatimModuleSyntax, regula devine simplă: orice import fără type rămâne în JavaScript, orice import type (sau import { type X }) dispare. Un tip importat fără type dă eroarea TS1484. Opțiunea pornește și isolatedModules și înlocuiește opțiunile scoase importsNotUsedAsValues și preserveValueImports.

Opțiunea cere ca sintaxa modulelor să corespundă formatului real: într-un fișier CommonJS nu poți scrie import/export ES (erorile TS1295 și TS1287). Implicit: false; tsc --init din 5.9 o pornește.

Parametri

Parametru Descriere
true Importurile fără type sunt păstrate neschimbate; tipurile cer import type.
false Valoarea implicită: tsc decide singur ce importuri șterge.

Exemple

Exemplul 1: Tip importat fără type

Produs este o interfață. Cu opțiunea pornită, trebuie importat cu import type.

// tsconfig.json
{
  "compilerOptions": {
    "verbatimModuleSyntax": true,
    "module": "nodenext",
    "target": "es2022"
  }
}

// index.ts
import { Produs, MONEDA } from "./tipuri.js";

const p: Produs = { nume: "carte", pret: 45 };
console.log(p.nume, p.pret, MONEDA);

Rezultat (tsc 5.9)

$ npx tsc --noEmit
index.ts(1,10): error TS1484: 'Produs' is a type and must be imported using a type-only import when 'verbatimModuleSyntax' is enabled.

Exemplul 2: Ce rămâne în JavaScript

nefolosit nu este folosit, dar importul rămâne, deci efecte.ts rulează. Fără opțiune (--verbatimModuleSyntax false), tsc ar șterge importul și mesajul „efecte.ts a rulat” nu ar mai apărea.

// efecte.ts
console.log("efecte.ts a rulat");
export const nefolosit = 1;

// index.ts
import type { Produs } from "./tipuri.js";
import { MONEDA } from "./tipuri.js";
import { nefolosit } from "./efecte.js";

const p: Produs = { nume: "carte", pret: 45 };
console.log(p.nume, p.pret, MONEDA);

Rezultat (tsc 5.9)

$ npx tsc && cat index.js
import { MONEDA } from "./tipuri.js";
import { nefolosit } from "./efecte.js";
const p = { nume: "carte", pret: 45 };
console.log(p.nume, p.pret, MONEDA);

$ node index.js
efecte.ts a rulat
carte 45 lei

Exemplul 3: import type în cod care rulează

Stats este doar un tip din node:fs, deci este importat cu import type; statSync și writeFileSync sunt funcții reale.

import type { Stats } from "node:fs";
import { statSync, writeFileSync } from "node:fs";

writeFileSync("note.txt", "Ana: 9\nIon: 10\n");

function descrie(s: Stats): string {
  return s.isFile() ? `fișier de ${s.size} octeți` : "nu este fișier";
}

console.log("note.txt:", descrie(statSync("note.txt")));

Rezultat

note.txt: fișier de 15 octeți

Greșeli frecvente

  • Să pornești opțiunea cu "module": "nodenext" fără "type": "module" în package.json. Fișierele .ts sunt CommonJS și orice import dă TS1295.
  • Să scrii import { type A, type B } from "./x.js" când toate numele sunt tipuri. Importul gol import {} from "./x.js" rămâne în JavaScript; scrie import type { A, B }.

Comenzi înrudite

  • isolatedModules: Dă eroare pentru codul care nu poate fi transformat corect fișier cu fișier (de esbuild, Babel, swc sau Node.js).
  • importsNotUsedAsValues: Opțiuni scoase din TypeScript 5.5 care controlau păstrarea importurilor; înlocuite de verbatimModuleSyntax.
  • module: Alege formatul de module din JavaScript-ul generat (ES modules, CommonJS) și regulile de import pe care le verifică tsc.
  • erasableSyntaxOnly: Interzice construcțiile TypeScript care generează cod (enum, namespace, parametri-proprietate), ca Node.js să poată rula direct fișierele .ts.