Babel preset-typescript and tsc type-checking responsibility at transpilation boundary
0 reputation · 18 Nov 2024, 21:54 UTC
0 reputation · 18 Nov 2024, 21:54 UTC
Determine whether a Babel-only pipeline using @babel/preset-typescript on Babel 7 guarantees type-safe JavaScript output or requires a separate TypeScript type-check step before release.
Babel erases TypeScript annotations without static type checking and does not validate type compatibility across imports. It may also resolve tsconfig path mappings differently than tsc.
@babel/preset-typescript does not perform type checking. It only erases TypeScript syntax and transpiles to JavaScript. A separate tsc --noEmit step is required to catch type errors before release. Babel and tsc resolve module paths independently, so aliases must be configured in both tsconfig.json and your Babel configuration.
import type declarations.const x: number = 'hello', foo.bar() where bar doesn't exist, or mismatched generics.Add tsc --noEmit (or tsc --noEmit --skipLibCheck for faster CI) as a gate before your Babel build. In a typical CI pipeline:
# 1. Type check (fails on any type error)
npx tsc --noEmit
# 2. Transpile with Babel (only runs if step 1 passes)
npx babel src --out-dir dist
During development, run tsc --noEmit --watch in a separate terminal for fast feedback without duplicating emit work.
Babel does not read tsconfig.json paths. You must mirror the same aliases in your Babel config using babel-plugin-module-resolver (or the plugins array in babel-preset-typescript options).
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["src/*"],
"@components/*": ["src/components/*"]
},
"isolatedModules": true
}
}
module.exports = {
presets: [
["@babel/preset-typescript", { allowNamespaces: true }]
],
plugins: [
["module-resolver", {
root: ["."],
alias: {
"@": "./src",
"@components": "./src/components"
}
}]
]
};
With both configured identically, import Button from '@/components/Button' resolves to the same file for tsc and Babel.
"isolatedModules": true — Required. Ensures each file can be transpiled independently (no cross-file emit dependencies like const enum or namespace merging)."verbatimModuleSyntax": true (TS 5.0+) — Keeps import type imports explicit so Babel can strip them correctly when onlyRemoveTypeImports: true is set in the preset."skipLibCheck": true — Optional in CI for speed, but disable for release builds to catch declaration-file mismatches.const n: number = 'oops').npx tsc --noEmit — it should fail with TS2322.tsc --noEmit and Babel; confirm they produce identical resolved paths (check dist output or use --traceResolution with tsc).tsc --noEmit as a required CI step before the Babel build.If you use a bundler (webpack, Vite, esbuild, Rollup), its own alias/resolution config must also match. The research above covers only the Babel↔tsc boundary. Verify your bundler's resolve.alias / resolve.extensions mirrors the same mappings.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.