Nine times out of ten, TS1056, TS1028, or TS2378 on a getter or setter means "target": "ES3" in tsconfig.json. TypeScript won't emit accessors below ES5 because they compile to Object.defineProperty — an ES5 primitive. Set "target": "ES5" or higher and rebuild.
If the error persists after that, there are two more cases below — getter returning undefined, and a type mismatch between getter and setter.
The errors
You'll see one or more of these errors in your terminal or IDE when compiling or saving a file:
error TS1056: Accessors are only available when targeting ECMAScript 5 and higher.
error TS1028: 'get' and 'set' accessors are not allowed in an ambient context.
error TS2378: A 'get' accessor must return a value.
It happens when you define a getter or setter inside a class, like this:
class User {
private _name: string;
get name(): string {
return this._name;
}
set name(value: string) {
this._name = value;
}
}
Here, private _name is a backing field guarded by TypeScript's property visibility modifiers—public, private, and protected—which control who can read or write each member. The public getter methods and setter methods form a controlled read/write surface around that private field, so callers interact with user.name instead of touching _name directly. When any part of this contract breaks—an ECMAScript target version that's too low, ambient-context misuse, or a mismatch between the getter's and setter's types—the compiler raises one of the three errors above.
Why it happens
TypeScript's get and set accessors are syntactic sugar over JavaScript's Object.defineProperty, which only works reliably in ES5 and later. If your tsconfig.json compiler options set "target" to an ECMAScript target version like "ES3", or to a higher version like "ES2015" without enabling "lib": ["ES5"], the compiler refuses to emit accessor syntax because it cannot guarantee runtime support.
// src/types/user.ts
export class User {
private _name: string;
// This line triggers TS1056 if target < ES5
get name(): string {
return this._name;
}
// This line triggers TS2378 if return type is void or missing
set name(value: string) {
this._name = value;
}
}
TypeScript also forbids accessors in ambient contexts—like .d.ts declaration files or declare class blocks—because they describe runtime behavior that must be implemented elsewhere. For example:
// types/user.d.ts
declare class User {
get name(): string; // ❌ TS1028: not allowed in ambient context
set name(value: string);
}
This fails because declaration files must declare signatures, not implementations. The compiler enforces this to prevent accidental omission of accessor logic.
The fix — bump your ES target
{
"compilerOptions": {
"target": "ES5",
"lib": ["ES5", "DOM"],
"outDir": "./dist",
"rootDir": "./src"
}
}
That single change addresses the cause because ES5+ targets include native support for getter/setter syntax, and TypeScript's emitter now safely converts them to Object.defineProperty calls.
Step by step
Open
tsconfig.json.Locate the
"compilerOptions"block.Add or update
"target": "ES5"(or"ES2016","ES2020", etc.).Ensure
"lib"includes"ES5"or higher (e.g.,"ES2015"implies ES5+).Save and restart your dev server (
npm run dev) or re-runtsc.
For declaration files, replace accessors with property signatures:
// types/user.d.ts
declare class User {
name: string; // ✅ Ambient property, no get/set
}
Then implement the actual class in a .ts file:
// src/user.ts
import { User as UserDef } from '../types/user';
export class User implements UserDef {
private _name: string;
get name(): string {
return this._name;
}
set name(value: string) {
this._name = value;
}
}
Check it
npx tsc --noEmit
Found 0 errors. — done. Still seeing errors? Two other variants:
Variant A — Getter returns undefined or void
TypeScript infers the return type from the body. If your getter has no return statement, or returns void, you'll get TS2378. These accessor return types must be explicit and consistent: the getter method's return type defines the property's read type, and the setter method's parameter type defines the write type. When those accessor return types and parameter types drift apart, the compiler raises TS2378 to prevent silent coercion bugs.
Fix by ensuring the getter returns a value matching the expected type:
class Config {
private _debug = false;
get debug(): boolean {
return this._debug; // ✅ Explicit return
}
set debug(value: boolean) {
this._debug = value;
}
}
Variant B — Setter parameter type mismatch
If the setter's parameter type is incompatible with the getter's return type, TypeScript reports TS2378 or TS2345.
Fix by aligning types:
class Temperature {
private _celsius = 0;
get celsius(): number {
return this._celsius;
}
// ❌ Wrong: setter expects string, getter returns number
// set celsius(value: string) { this._celsius = parseFloat(value); }
// ✅ Correct: both use number
set celsius(value: number) {
this._celsius = value;
}
}
Preventing regressions
Below ES5, TypeScript falls back to __defineGetter__ and __defineSetter__ — deprecated, non-standard, and the wrong choice for anything targeting modern runtimes. ES5+ guarantees Object.defineProperty, which is what you want.
To catch a future target drift in CI, add this ESLint rule:
{
"rules": {
"@typescript-eslint/ban-ts-comment": "error",
"no-undef": "off"
},
"parserOptions": {
"ecmaVersion": 2020
}
}
Also, run tsc --noEmit in your CI pipeline. I cover this in detail in JS to TypeScript: Incremental Migration, No Full Rewrite, where I show how to gradually adopt accessors without breaking existing code.
For deeper type safety, especially when working with Supabase or Next.js, see Complete Type Safety Guide for Next.js and Supabase with TypeScript.
Related
What is TypeScript and why should I use it instead of JavaScript
Complete Type Safety Guide for Next.js and Supabase with TypeScript
With these adjustments—appropriate ECMAScript target versions, well-configured tsconfig.json compiler options, carefully chosen property visibility modifiers, and aligned accessor return types—your TypeScript accessors, including getter methods and setter methods, will compile cleanly and behave predictably at runtime, keeping your build green.
Originally published at https://www.iloveblogs.blog
Top comments (0)