Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What does the TypeScript `accessor` keyword do?

I stumbled upon the accessor keyword in TypeScript:

class Example {
  accessor value: number
}

What does it do? I don't see any difference in usage of new Example().value.

like image 657
mrousavy Avatar asked Oct 26 '25 15:10

mrousavy


2 Answers

I compared the compiled JS output of TypeScript, and there seems to be an actual difference:

Simple property

TypeScript input:

class Example {
    value: number = 5
}

TypeScript output:

"use strict";
class Example {
    constructor() {
        this.value = 5;
    }
}

Accessor property

TypeScript input:

class Example {
    accessor value: number = 5
}

TypeScript output:

"use strict";
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
    if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
    if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
    return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
};
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
    if (kind === "m") throw new TypeError("Private method is not writable");
    if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
    if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
    return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
};
var _Example_value_accessor_storage;
class Example {
    constructor() {
        _Example_value_accessor_storage.set(this, 5);
    }
    get value() { return __classPrivateFieldGet(this, _Example_value_accessor_storage, "f"); }
    set value(value) { __classPrivateFieldSet(this, _Example_value_accessor_storage, value, "f"); }
}
_Example_value_accessor_storage = new WeakMap();

So apparently accessor generates a private JS property for #value, and a getter + setter. While this seems useless for Example itself, it is useful for subclasses, because you can override the generated getter or setter for value in a subclass:

class Example {
    accessor value: number = 5
}
class Subclass extends Example {
    override get value(): number {
        return 1
    }
}

If value would not be an accessor (but instead a simple property), you cannot override get value() since it's not a property. TIL

like image 153
mrousavy Avatar answered Oct 29 '25 05:10

mrousavy


It's not really a TypeScript keyword per se; instead, it's a keyword that will be introduced to JavaScript once the tc39/proposal-decorators proposal reaches Stage 4 of the TC39 process. TypeScript tends to support features once they reach Stage 3 (at which point it is reasonably sure to eventually make it into JavaScript), and since tc39/proposal-decorators is Stage 3, they have implemented it in TypeScript; you can read about it in the TypeScript 4.9 release notes.

The relevant JavaScript feature is for making auto accessors, which automatically add a getter and a setter for the class field (using the equivalent of a private field for the actual storage).


One of TypeScript's major purposes is allowing you to write "tomorrow's JavaScript today", so if your --target version of JavaScript is older than necessary for a language feature, TypeScript will often downlevel that language feature to code that works for the target version. Hence if your target is any JavaScript too old to support auto accessors (and right now that's any fixed version of JavaScript), TypeScript will downlevel:

// @target: ES2024
class Example {
    accessor value: number = 5
}

produces

"use strict";
// @target: ES2024
class Example {
    #value_accessor_storage = 5;
    get value() { return this.#value_accessor_storage; }
    set value(value) { this.#value_accessor_storage = value; }
}

Playground link to code

but again, auto accessors are not a TypeScript-specific feature. If your --target is ESNext, meaning the latest-and-greatest features are assumed to be present, there is no downleveling:

// @target: ESNext
class Example {
    accessor value: number = 5
}

produces

"use strict";
// @target: ESNext
class Example {
    accessor value = 5;
}

Playground link to code

like image 43
jcalz Avatar answered Oct 29 '25 05:10

jcalz