| | |

Parcel 12 ๐Ÿ“ฆ TypeScript โ€” SWC-based transpilation

โฑ๏ธ Estimated read: 10 min | |

Parcel transpiles TypeScript out of the box, with no configuration required. When it encounters a .ts or .tsx file, it strips the type annotations and converts the remaining code to JavaScript. The default transpiler is not the TypeScript compiler (tsc) and not Babelโ€”it is SWC, a Rust-based compiler that Parcel adopted in the Parcel 2 beta releases. The official Parcel 2 beta 3 announcement states that SWC replaces Babel by default for transpilation, including “compiling JSX and TypeScript,” and that SWC is roughly 20x faster than Babel . The language documentation confirms the behavior: Parcel “automatically transpiles TypeScript whenever you use a .ts or .tsx file,” and “strips the types to convert TypeScript to JavaScript” .

This chapter covers the SWC default, the isolatedModules constraint that follows from Parcel’s per-file processing, the tsconfig.json options that Parcel actually reads, and the alternative transformers that a project can opt into when the default is not sufficient.

Key point: Parcel does not type-check TypeScript. The default SWC transformer strips the types and transpiles the syntax, but it does not verify that the types are correct. The recommended type-checking workflow is the editor for immediate feedback and tsc --noEmit in CI, run separately from the Parcel build .


Why SWC is the default

The speed problem. TypeScript’s official compiler is thorough but not fast. Babel is faster, but it is a JavaScript-based tool with its own parse and transform overhead. SWC is written in Rust and performs the same parse, transform, and code-generation steps with native performance. The Parcel 2 beta announcement describes SWC as the foundation of the new JavaScript transformer and notes that it replaces Babel for TypeScript compilation by default . The result is that a Parcel build with TypeScript files does not pay the Babel cost unless the project explicitly configures a Babel preset.

The zero-configuration problem. Parcel detects the .ts and .tsx extensions and applies the SWC-based transformer automatically. The project does not need a .babelrc, a ts-loader, or a ts-jest configuration. The types are stripped, the modern syntax is downleveled according to the project’s browserslist, and the JSX is converted. The only configuration that Parcel reads from tsconfig.json is a small subset: the JSX options, experimentalDecorators, and useDefineForClassFields .

The Babel fallback problem. SWC is the default, but Babel is still supported. If a .babelrc or babel.config.js containing @babel/preset-typescript is found, Parcel uses Babel to compile the TypeScript files instead of SWC . This is the escape hatch for a project that depends on a Babel plugin that SWC does not implement.


a. The isolatedModules constraint

Parcel processes each file individually. It does not build a cross-file type graph before transpiling, which means some TypeScript features that require type information from another file cannot be compiled. The documentation states this explicitly: Parcel “implicitly enables the isolatedModules option,” and features like const enum that require cross-file type information “will not work” .

The practical consequence is that the code must be written in a way that each file is self-contained from the transpiler’s perspective. A const enum is not supported; a regular enum is. A re-export of a type must use export type so that the transpiler does not attempt to emit a runtime value for it. The documentation recommends enabling isolatedModules in the project’s tsconfig.json so that the IDE and the type-checker warn about these constructs before the Parcel build fails .


b. The tsconfig.json options that Parcel reads

Parcel does not read the full tsconfig.json. It reads the options that affect the transpilation and ignores the rest. The documentation lists the supported options: the JSX options, experimentalDecorators, and useDefineForClassFields . The target and module options are not used, because Parcel’s own browserslist and output format determine those. The baseUrl and paths options are not supported for module resolution; the documentation recommends Parcel’s own tilde or absolute specifiers as the alternative .

A project that depends on paths for import aliases has two options. The parcel-resolver-tspaths plugin adds tsconfig path alias resolution to Parcel v2, and it is installed as a resolver in .parcelrc . The alternative is to use Parcel’s own alias configuration, which is the supported path. The plugin is a community workaround, not a built-in feature.


c. The alternative transformers

Parcel’s default is SWC, but two other transformers are available for projects with specific requirements.

The @parcel/transformer-typescript-tsc plugin uses the official TypeScript compiler instead of SWC. It is the choice when the project uses a tsconfig.json option that SWC does not support, such as a custom target or a compiler option that affects the emit. The plugin is added to the transformers section of .parcelrc for the *.{ts,tsx} pattern . Even with this transformer, Parcel still processes each file individually, so the isolatedModules constraint applies, and the transformer does not perform type checking .

The Babel transformer is used when a .babelrc with @babel/preset-typescript is present. It has the same isolatedModules caveat as the other transformers, and it is the slowest of the three options .

The @parcel/validator-typescript plugin is an experimental type-checking validator. It runs in the background after the bundles are generated, and it requires the include option in tsconfig.json to list all the source files . It is not a replacement for a CI type-check step; it is a convenience for the developer who wants the errors to appear during the Parcel build.


Complete Example Session

// ============================================
// PART 1: THE TYPESCRIPT FILE
// ============================================
// src/app.ts
interface User {
  id: string
  name: string
}

export function greet(user: User): string {
  return `Hello, ${user.name}`
}
// ============================================
// PART 2: THE JAVASCRIPT IMPORT
// ============================================
// src/index.js
import { greet } from './app.ts'

console.log(greet({ id: '1', name: 'Alice' }))
# ============================================
# PART 3: THE BUILD
# ============================================
parcel build src/index.js

# Parcel detects the .ts file.
# SWC strips the types and transpiles the syntax.
# The output is JavaScript.
// ============================================
// PART 4: THE TSCONFIG
// ============================================
{
  "compilerOptions": {
    "jsx": "react-jsx",
    "experimentalDecorators": true,
    "isolatedModules": true
  }
}
// ============================================
// PART 5: THE CONST ENUM FAILURE
// ============================================
// const enum Colors { Red, Green }
// Parcel's SWC transformer does not support const enum.
// Use a regular enum or a plain object instead.
// ============================================
// PART 6: THE TYPE RE-EXPORT
// ============================================
// Wrong: export { User } from './types'
// Right: export type { User } from './types'
// ============================================
// PART 7: THE TSC TRANSFORMER
// ============================================
// .parcelrc
{
  "extends": "@parcel/config-default",
  "transformers": {
    "*.{ts,tsx}": ["@parcel/transformer-typescript-tsc"]
  }
}
# ============================================
# PART 8: THE TYPE-CHECK STEP
# ============================================
# package.json
# "scripts": {
#   "build": "parcel build src/index.js",
#   "typecheck": "tsc --noEmit"
# }

npm run typecheck
// ============================================
// PART 9: THE PATHS PLUGIN
// ============================================
// .parcelrc
{
  "extends": "@parcel/config-default",
  "resolvers": ["parcel-resolver-tspaths"]
}
// ============================================
// PART 10: THE DECISION
// ============================================
// Default โ†’ SWC, fast, no type-checking
// Unsupported tsconfig โ†’ @parcel/transformer-typescript-tsc
// Babel plugin needed โ†’ .babelrc with @babel/preset-typescript
// Type-checking โ†’ editor + tsc --noEmit in CI

The ten parts covered the TypeScript file, the JavaScript import, the build, the tsconfig, the const enum failure, the type re-export, the TSC transformer, the type-check step, the paths plugin, and the decision.


Quick Reference

Transpiler options

TransformerTriggerType-checking
SWC (default).ts, .tsxNo
Babel.babelrc with @babel/preset-typescriptNo
TSC@parcel/transformer-typescript-tsc in .parcelrcNo

tsconfig options read by Parcel

OptionSupported
JSX optionsYes
experimentalDecoratorsYes
useDefineForClassFieldsYes
targetNo (use browserslist)
moduleNo
baseUrl / pathsNo (use plugin or Parcel aliases)

The isolatedModules constraints

ConstructSupported
Regular enumYes
const enumNo
export typeYes
export { Type }No

Type-checking workflow

ContextTool
EditorTypeScript language service
CItsc --noEmit
Parcel buildNo type-checking

Best Practices

โœ… Do This:

// Use export type for type re-exports
export type { User } from './types'                          // โœ…
// Use a regular enum instead of const enum
enum Colors { Red, Green }                                   // โœ…
// Enable isolatedModules in tsconfig
{ "compilerOptions": { "isolatedModules": true } }            // โœ…
# Run tsc --noEmit in CI
npm run typecheck                                            // โœ…

โŒ Don’t Do This:

// Use const enum with Parcel's default transformer
const enum Colors { Red }                                    // โŒ
// Re-export a type as a value
export { User } from './types'                               // โŒ
# Assume Parcel type-checks the code
parcel build src/index.js                                    // โŒ (no type-checking)

Common Pitfalls

PitfallWhy It HappensFix
const enum failsSWC does not support itUse a regular enum
Type re-export failsThe type is emitted as a valueUse export type
paths aliases do not resolveParcel does not read pathsUse parcel-resolver-tspaths or Parcel aliases
Type errors reach productionParcel does not type-checkRun tsc --noEmit in CI
Build uses Babel instead of SWCA .babelrc with @babel/preset-typescript existsRemove the preset or accept the Babel path

Real-World Examples

1. Default TypeScript import

import { greet } from './app.ts'

2. The isolatedModules re-export

export type { User } from './types'

3. The TSC transformer

{ "transformers": { "*.{ts,tsx}": ["@parcel/transformer-typescript-tsc"] } }

4. The type-check script

{ "scripts": { "typecheck": "tsc --noEmit" } }

5. The paths plugin

{ "resolvers": ["parcel-resolver-tspaths"] }

6. The tsconfig options

{ "compilerOptions": { "jsx": "react-jsx", "experimentalDecorators": true } }

7. The const enum failure

// const enum Colors { Red } // not supported

8. The regular enum

enum Colors { Red, Green }

9. The Babel fallback

// .babelrc with @babel/preset-typescript

10. The decision

Default โ†’ SWC; unsupported tsconfig โ†’ TSC; Babel plugin โ†’ Babel.

Visual

The transpilation pipeline

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  app.ts                                                      โ”‚
โ”‚       โ”‚                                                      โ”‚
โ”‚       โ–ผ                                                      โ”‚
โ”‚  Parcel detects .ts                                          โ”‚
โ”‚       โ”‚                                                      โ”‚
โ”‚       โ–ผ                                                      โ”‚
โ”‚  SWC transformer                                             โ”‚
โ”‚    โ”œโ”€โ”€ strip types                                           โ”‚
โ”‚    โ”œโ”€โ”€ transpile syntax                                      โ”‚
โ”‚    โ””โ”€โ”€ convert JSX                                           โ”‚
โ”‚       โ”‚                                                      โ”‚
โ”‚       โ–ผ                                                      โ”‚
โ”‚  JavaScript output                                           โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

The type-checking boundary

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  Parcel build                                                โ”‚
โ”‚    โ””โ”€โ”€ transpiles, no type-checking                          โ”‚
โ”‚                                                              โ”‚
โ”‚  Editor                                                      โ”‚
โ”‚    โ””โ”€โ”€ type errors as you type                               โ”‚
โ”‚                                                              โ”‚
โ”‚  CI                                                          โ”‚
โ”‚    โ””โ”€โ”€ tsc --noEmit                                          โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

The isolatedModules constraints

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  SUPPORTED                    NOT SUPPORTED                  โ”‚
โ”‚                                                              โ”‚
โ”‚  enum                         const enum                    โ”‚
โ”‚  export type { T }            export { T } (type)            โ”‚
โ”‚  import type { T }            import { T } (type)            โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

The transformer selection

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  Default โ†’ SWC                                               โ”‚
โ”‚  Unsupported tsconfig โ†’ @parcel/transformer-typescript-tsc   โ”‚
โ”‚  Babel plugin โ†’ .babelrc with @babel/preset-typescript       โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Summary

ItemValue
Default transpilerSWC
Trigger.ts, .tsx extension
Type-checkingNot performed
isolatedModulesImplicitly enabled
const enumNot supported
tsconfig options readJSX, experimentalDecorators, useDefineForClassFields
paths resolutionNot supported; use plugin
TSC transformer@parcel/transformer-typescript-tsc
Babel fallback.babelrc with @babel/preset-typescript
Type-checking workflowEditor + tsc --noEmit in CI

Key takeaways:

  • Parcel uses SWC for TypeScript transpilation by default. The Rust-based compiler strips the types and transpiles the syntax faster than Babel or TSC, and it requires no configuration. The .ts and .tsx extensions are the trigger .
  • Parcel does not type-check TypeScript. The default transformer, the TSC transformer, and the Babel transformer all strip the types without verifying them. The recommended workflow is the editor for immediate feedback and tsc --noEmit in CI, run separately from the Parcel build .
  • The isolatedModules constraint is implicit. Parcel processes each file individually, so const enum and other cross-file type-dependent features do not work. Enable isolatedModules in tsconfig.json so the IDE warns about the unsupported constructs .
  • Parcel reads a small subset of tsconfig.json. The JSX options, experimentalDecorators, and useDefineForClassFields are supported. target, module, baseUrl, and paths are not. The parcel-resolver-tspaths plugin is the community workaround for paths .
  • The TSC transformer is the escape hatch for unsupported options. The @parcel/transformer-typescript-tsc plugin uses the official TypeScript compiler for the transpilation, but it still processes each file individually and does not type-check .
  • The Babel fallback is for projects that need a Babel plugin. A .babelrc with @babel/preset-typescript switches the TypeScript compilation to Babel, which is slower than SWC but supports the Babel plugin ecosystem .

Remember: Parcel’s TypeScript support is a transpiler, not a compiler. The SWC default strips the types and converts the syntax, and it does so without configuration and without type-checking. The isolatedModules constraint is the price of the per-file processing: const enum and unqualified type re-exports do not work. The type-checking step is separate: the editor for immediate feedback, and tsc --noEmit in CI. The TSC and Babel transformers exist for the projects that need them, but they share the same per-file limitation and the same absence of type-checking.

Was this helpful?


Stop using slow, ad-bloated tool sites! ๐Ÿคฎ

๐Ÿ”Ž Search “KandZ Tools” on Google to use many professional utilities for free.

KandZ.me is the ultimate minimalist hub for:
โœ… Finance (Mortgage, Interest, Inflation)
โœ… Tech (Base64, JSON, Dev Suite, IP)
โœ… Health (BMI, BMR, TDEE)
โœ… Productivity (Timer, Workspace, QR)

โšก๏ธ Fast & Private
๐Ÿ”’ No data leaves your device
๐Ÿ’Ž 100% Free

๐Ÿ”— Use it now: https://tools.kandz.me
๐Ÿ”– Bookmark itโ€”youโ€™ll need it later!