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.
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
Function expression requires a return type
Function expression requires a return type
Value of type 'string | undefined' is not iterable
How to fix "Value is not iterable" in TypeScript
Type 'undefined' is not assignable to type 'string'
How to fix "Type undefined is not assignable to type string" in TypeScript
Type narrowing from typeof check produces 'never'
How to fix "Type narrowing produces never" in TypeScript
Type parameter 'T' has conflicting constraints
How to fix "Type parameter has conflicting constraints" in TypeScript