This error occurs when TypeScript compiler options conflict in your tsconfig.json file. The declaration option requires either emitDeclarationOnly or a normal emit mode, but noEmit prevents all file emission. The fix involves choosing one option strategy and removing conflicting settings.
Fixes TS5053
{
"compilerOptions": {
"noEmit": true // Pick ONE strategy - this disables ALL output
}
}{"compilerOptions": {"noEmit": true // Pick ONE strategy - this disables ALL output}}Option 'declaration' cannot be used without 'emitDeclarationOnly' when 'noEmit' is set
TypeScript's compiler options for file emission fall into distinct categories: - **noEmit**: Disables all output - no .js files, source maps, or declaration files - **emitDeclarationOnly**: Only emits .d.ts declaration files, no JavaScript - **declaration**: Emits both .js and .d.ts files When you have `"noEmit": true` in your configuration, TypeScript doesn't emit anything at all. If you also try to use `"declaration": true` (which requires some form of output), or `"emitDeclarationOnly": true` (which contradicts noEmit by emitting declarations), TypeScript throws error TS5053. This conflict typically arises when: 1. Your tsconfig.json extends a base configuration that sets noEmit 2. You're overriding those settings inconsistently 3. You have conflicting compiler options passed via CLI and config file
First, determine what you need:
- Type-checking only (CI/CD linting): Use "noEmit": true
- Generate declarations only (type-only packages): Use "emitDeclarationOnly": true
- Generate both .js and .d.ts (libraries/apps): Use "declaration": true (with noEmit: false or omitted)
Add this to your tsconfig.json based on your use case:
{
"compilerOptions": {
"noEmit": true // Pick ONE strategy - this disables ALL output
}
}OR
{
"compilerOptions": {
"emitDeclarationOnly": true, // Emit only .d.ts files
"declaration": true
}
}OR
{
"compilerOptions": {
"declaration": true, // Emit .d.ts alongside .js
"noEmit": false // Explicitly allow emission (or omit, default is false)
}
}Open your tsconfig.json and ensure you don't have both noEmit and emitDeclarationOnly:
WRONG:
{
"compilerOptions": {
"noEmit": true,
"emitDeclarationOnly": true // These conflict!
}
}WRONG:
{
"compilerOptions": {
"noEmit": true,
"declaration": true // This requires output!
}
}CORRECT (type-checking only):
{
"compilerOptions": {
"noEmit": true
}
}CORRECT (declarations only):
{
"compilerOptions": {
"emitDeclarationOnly": true,
"declaration": true
}
}If your tsconfig extends a base config, the conflict may come from there:
{
"extends": "./tsconfig.base.json",
"compilerOptions": {
"emitDeclarationOnly": true // Overrides noEmit from base
}
}Check what's in tsconfig.base.json:
cat tsconfig.base.json | grep -A5 '"compilerOptions"'If the base config has "noEmit": true, you must explicitly override:
{
"extends": "./tsconfig.base.json",
"compilerOptions": {
"noEmit": false, // Override inherited noEmit
"emitDeclarationOnly": true,
"declaration": true
}
}In a monorepo with multiple packages, create separate TypeScript configs:
tsconfig.json (shared base, for type-checking):
{
"compilerOptions": {
"noEmit": true,
"target": "ES2020",
"module": "ESNext"
}
}tsconfig.build.json (for building libraries):
{
"extends": "./tsconfig.json",
"compilerOptions": {
"noEmit": false,
"declaration": true,
"outDir": "./dist"
},
"include": ["src/**/*"]
}Then in your build script:
# Type check (no output)
tsc --noEmit
# Build (emits .js and .d.ts)
tsc --project tsconfig.build.jsonTest your configuration:
# Check for errors (no output files created)
npx tsc --noEmit
# Or build if using emitDeclarationOnly
npx tscIf successful, you should see no TS5053 error. Check that:
- Type-checking passes
- If you have emitDeclarationOnly, .d.ts files are created
- If you have declaration: true, both .js and .d.ts files are created
- If you have noEmit: true, no output directory is created
### Understanding the Three Options
noEmit: true
- Use case: CI/CD type-checking, linting, IDE checking
- Output: None (zero files written)
- Common in: GitHub Actions workflows, pre-commit hooks, test suites
emitDeclarationOnly: true
- Use case: Pure TypeScript libraries that need .d.ts files only
- Output: Only .d.ts files (no .js)
- Requires: Some other tool (Babel, swc, esbuild) to handle JavaScript transpilation
- Common in: Libraries with separate JS build process, monorepo packages
declaration: true (default noEmit: false)
- Use case: Applications and libraries with direct TypeScript output
- Output: Both .js and .d.ts files
- Common in: Traditional build setups, full-stack applications
### CLI Override Behavior
If you run tsc with CLI flags, they override tsconfig settings. Be careful with:
# This conflicts if tsconfig has emitDeclarationOnly
npx tsc --noEmit
# This conflicts if tsconfig has noEmit
npx tsc --emitDeclarationOnlyTo avoid surprises, specify the strategy in tsconfig.json and let scripts use it:
npx tsc # Uses tsconfig.json settings
npx tsc --project tsconfig.build.json # Uses alternate config### Multi-package monorepos
Nx, Turborepo, and similar tools often set conflicting configs. If you get this error in a monorepo:
1. Check the root tsconfig.json and any extended configs
2. Ensure each package's tsconfig.json is explicit about its emit strategy
3. Use the tool's documentation (Nx: project.json, Turborepo: turbo.json) to override compiler options