TS1109Syntax Error
Since TS 1.0

Fix TS1109: Expression Expected

Learn why TypeScript throws TS1109 when a value is missing where the parser needs one: export default const, dangling operators, JSX in a .ts file.

error TS1109: Expression expected

What This Error Means

TS1109 means the TypeScript parser was standing in a spot where only a value makes sense — the right-hand side of an =, the operand after a *, the thing after return, a property value, the inside of a JSX {} — and the next token could not possibly start one. A semicolon, a closing brace, a comma, or a keyword like const shows up instead, and the parser gives up on the expression it was building.

This is a parse error, not a type error. It happens on the very first pass over your source, before TypeScript knows anything about your types, so no compiler flag changes the outcome: strict, target, module and skipLibCheck are all irrelevant. What does change the outcome is the compiler version — a chunk of TS1109 reports in the wild are perfectly valid modern syntax being read by an old parser.

// The general shape of the error:
const retryDelayMs = 250 *
//                        ~ Error: Expression expected.

Unlike TS1128 (Declaration or statement expected), which usually points somewhere after the real mistake, TS1109 is normally accurate: the fix is at or immediately before the reported column. The error rarely travels alone, though — once the parser is out of step with your code it keeps reporting, so you will often see ';' expected (TS1005) or Identifier expected (TS1003) underneath. Fix the first TS1109 and re-run before you read the rest of the list.

Common Causes

1. export default const in a Single Statement

export default takes an expression — a value to export. const starts a declaration, which is a statement, so the parser hits const while it is still waiting for a value.

// ❌ Broken
export default const apiBaseUrl = "https://api.example.com"
//                   ~~~~~ Error: Expression expected.
// ✅ Fixed — declare first, then export the binding
const apiBaseUrl = "https://api.example.com"
export default apiBaseUrl

The same restriction rules out export default let, export default var and export default interface. export default function and export default class are allowed, because the language defines a special form for them. If you want a named export instead, drop default entirely and write export const apiBaseUrl = "https://api.example.com".

2. A Missing Operand After an Edit

Every binary operator needs something on both sides, and every object property needs a value. Delete one during a refactor and the next token — usually ;, , or } — lands where the parser wanted an expression.

// ❌ Broken
const retryDelayMs = 250 * ;
//                         ~ Error: Expression expected.
 
const userProfile = { name: "Ada", role: }
//                                       ~ Error: Expression expected.
// ✅ Fixed — give each operator and each property a value
const attempt = 3
const retryDelayMs = 250 * attempt
const userProfile = { name: "Ada", role: "admin" }

This is the shape you get from an interrupted edit or a bad merge: a half-typed line, a deleted variable that left its operator behind, a conflict resolution that swallowed the right-hand side. Because the parser points straight at the offending token, the column number in the error is the fastest way to find it.

3. JSX in a .ts File, or an Import That Stops Mid-Statement

TypeScript decides how to parse angle brackets based on the file extension. In a .ts file, <section ...> is read as a type assertion, not as JSX, and the attributes after it become nonsense to the parser.

// ❌ Broken — dashboard.ts
export const Panel = () => <section className="panel" />
//                                                    ~ Error: Expression expected.
//                                                      (after a cascade of TS1005 on the same line)
// ✅ Fixed — rename the file to dashboard.tsx
export const Panel = () => <section className="panel" />

Renaming is only half the fix: your tsconfig.json also needs a jsx setting — "jsx": "react-jsx" for a modern React setup, or "jsx": "preserve" when a bundler such as Next.js handles the transform.

An import that never reaches its module specifier produces the same code, because the parser treats what follows from as the start of an expression:

// ❌ Broken — the module path never got typed
import { formatCurrency } from
//                             ~ Error: Expression expected.
// ✅ Fixed — finish the specifier
import { formatCurrency } from "./money"

4. Modern Syntax on an Outdated TypeScript

If the code looks fine to you and to your editor, check which compiler is actually reading it. Optional chaining and nullish coalescing arrived in TypeScript 3.7, ??= in 4.0, template literal types in 4.1, #private in obj in 4.3, satisfies in 4.9. An older parser sees ? followed by . and reports a missing expression.

// ❌ Broken — only on TypeScript below 3.7
interface Order {
  shippingAddress?: { city?: string }
}
 
export function describeDestination(order: Order) {
  const city = order?.shippingAddress?.city ?? "unknown"
  //                ~ Error: Expression expected.
  return city
}
# ✅ Fixed — upgrade the compiler that reports the error
npm install --save-dev typescript@latest
# VS Code: "TypeScript: Select TypeScript Version" → Use Workspace Version

The same story explains TS1109 coming out of node_modules. A .d.ts file that ships template literal types — @types/babel__traverse is the classic — parses fine on a current compiler and reports "Expression expected" on the one your project pins. Upgrade TypeScript, or pin the @types package to a version that predates the syntax.

How to Fix It

  1. Go to the exact position the error reports. TS1109 points at the token that cannot start an expression, so the missing value belongs at or just before that column. Fix the first TS1109 in the file, save, and re-run — cascading TS1005 and TS1003 reports usually vanish with it.

  2. Split declarations away from export default. Anything of the form export default const|let|var|interface needs two statements: declare the binding, then export it. Reach for export const name = … when you did not actually want a default export.

  3. Give every operator and property a value. Scan the reported line for a dangling *, +, && or : — an operand that a refactor removed, a property key whose value never got typed, an import … from with no path.

  4. Check which TypeScript is reporting the error. Run npx tsc -v in the project and compare it to your editor's version (VS Code: TypeScript: Select TypeScript Version) and to whatever ts-jest, ts-node or CI uses. If the error only appears in one of them, the code is fine and the compiler is old — upgrade it rather than rewriting the syntax. Never rewrite ?. back into && chains to satisfy a stale parser.

  5. Rename JSX files to .tsx and keep jsx configured. A component in a .ts file will keep producing parse errors no matter how the markup is written. Set "jsx": "react-jsx" (or "preserve") in tsconfig.json once, and let the extension do the rest.

  6. Keep the compiler version pinned in devDependencies and shared everywhere. One typescript entry in package.json, workspace version selected in the editor, the same version in CI. That single habit removes the whole "it only fails on my machine" category of TS1109.

FAQ

What causes TypeScript error TS1109?

TS1109 is emitted by the parser, before any type checking happens. TypeScript's grammar has positions where only an expression is legal — after =, after an operator, after return or export default, inside (, [ or a JSX {}, and as an object property value. When the next token cannot begin an expression, you get "Expression expected".

The token that triggers it is usually a ;, }, , or ) left behind by an edit, or a keyword such as const that starts a declaration rather than a value. A third, less obvious cause is valid syntax that the compiler reading the file is too old to understand.

How do I fix Expression expected with export default const?

Write two statements instead of one:

const apiBaseUrl = "https://api.example.com"
export default apiBaseUrl

export default is followed by an expression, and const apiBaseUrl = … is a declaration, so the parser stops at const. The same applies to let, var and interface. If you did not need a default export in the first place, export const apiBaseUrl = "https://api.example.com" is a single statement that works.

Why does TS1109 appear only in my editor or only in CI?

Because more than one TypeScript version is involved. VS Code ships its own compiler and uses it for the red squiggles unless you run TypeScript: Select TypeScript Version and choose the workspace copy. Test runners and build tools resolve their own too, and a CI image may be several majors behind.

Syntax added in a recent release — ?. and ?? (3.7), ??= (4.0), template literal types (4.1), satisfies (4.9) — parses cleanly on a new compiler and reports TS1109 on an old one. Compare npx tsc -v with the version each tool reports before changing a single line of code; the fix is almost always an upgrade, not a rewrite.

Related Errors

Practice This

Browse all TypeScript practice challenges to keep sharpening your type-level skills.

Share this reference

Become a TypeScript Pro

Track your progress through 100+ hands-on challenges. Free, sign in with GitHub.

Or start solving right away: explore all TypeScript challenges