DEV Community

Cover image for TypeScript Getter Setter Errors: TS1056, TS1028, TS2378 Fix
Mahdi BEN RHOUMA
Mahdi BEN RHOUMA

Posted on • Originally published at iloveblogs.blog

TypeScript Getter Setter Errors: TS1056, TS1028, TS2378 Fix

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.

Enter fullscreen mode Exit fullscreen mode

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;

  }

}

Enter fullscreen mode Exit fullscreen mode

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;

  }

}

Enter fullscreen mode Exit fullscreen mode

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);

}

Enter fullscreen mode Exit fullscreen mode

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"

  }

}

Enter fullscreen mode Exit fullscreen mode

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

  1. Open tsconfig.json.

  2. Locate the "compilerOptions" block.

  3. Add or update "target": "ES5" (or "ES2016", "ES2020", etc.).

  4. Ensure "lib" includes "ES5" or higher (e.g., "ES2015" implies ES5+).

  5. Save and restart your dev server (npm run dev) or re-run tsc.

For declaration files, replace accessors with property signatures:


// types/user.d.ts

declare class User {

  name: string; // ✅ Ambient property, no get/set

}

Enter fullscreen mode Exit fullscreen mode

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;

  }

}

Enter fullscreen mode Exit fullscreen mode

Check it


npx tsc --noEmit

Enter fullscreen mode Exit fullscreen mode

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;

  }

}

Enter fullscreen mode Exit fullscreen mode

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;

  }

}

Enter fullscreen mode Exit fullscreen mode

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

  }

}

Enter fullscreen mode Exit fullscreen mode

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

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)