930

I'm trying to create get and set method for a property:

private _name: string;

Name() {
    get:
    {
        return this._name;
    }
    set:
    {
        this._name = ???;
    }
}

What's the keyword to set a value?

MuriloKunze
  • 15,195
  • 17
  • 54
  • 82
  • 15
    The underscore and PascalCase conflicts with the Typescript coding guidlines: https://github.com/Microsoft/TypeScript/wiki/Coding-guidelines – Niels Steenbeek Jan 12 '17 at 08:48
  • 4
    Hi @NielsSteenbeek - following the TypeScript contributors guidelines with properties and backing fields you'd end up with a name conflict. What's the suggested approach? – Jordan May 18 '17 at 09:20
  • 1
    Perhaps: ```typescript private name: string; getName() { get: { return this.name; } set: { this.name = ???; } } ``` – Jordan May 18 '17 at 09:22
  • 12
    Good thing those Typescript coding guidelines are pretty unattractive. I would only use them under coercion (e.g. I was paid to do so). – Thomas Eding Jun 30 '17 at 16:25
  • 37
    @NielsSteenbeek: did you read that document? "This is NOT a prescriptive guideline for the TypeScript community" – Jonathan Cast Apr 12 '18 at 01:22

12 Answers12

1446

TypeScript uses getter/setter syntax that is like ECMAScript4/ActionScript3.

class foo {
    private _bar: boolean = false;
    get bar(): boolean {
        return this._bar;
    }
    set bar(value: boolean) {
        this._bar = value;
    }
}

However, in order to use it at all, you must make sure the TypeScript compiler targets ECMAScript5 or higher. If you are running the command line compiler, use --target flag like this;

tsc --target ES5

If you are using Visual Studio, you must edit your project file to add the flag to the configuration for the TypeScriptCompile build tool. You can see that here:

That will produce this JavaScript, using the ECMAScript 5 Object.defineProperty() feature.

var foo = (function () {
    function foo() {
        this._bar = false;
    }
    Object.defineProperty(foo.prototype, "bar", {
        get: function () {
            return this._bar;
        },
        set: function (value) {
            this._bar = value;
        },
        enumerable: true,
        configurable: true
    });
    return foo;
})();

More recent versions of EcmaScript will produce code that looks more like the original TypeScript. For instance, targeting EcmaScript2017 will produce:

"use strict";
class foo {
    constructor() {
        this._bar = false;
    }
    get bar() {
        return this._bar;
    }
    set bar(value) {
        this._bar = value;
    }
}

So to use it,

var myFoo = new foo();
if(myFoo.bar) {         // calls the getter
    myFoo.bar = false;  // calls the setter and passes false
}

As @DanFromGermany suggests below, if your are simply reading and writing a local property like foo.bar = true, then having a setter and getter pair is overkill. You can always add them later if you need to do something, like logging, whenever the property is read or written.

Getters can be used to implement readonly properties. Here is an example that also shows how getters interact with readonly and optional types.

//
// type with optional readonly property.
// baz?:string is the same as baz:string|undefined
//
type Foo = {
    readonly bar: string;
    readonly baz?: string;
}
const foo:Foo = {bar: "bar"}
console.log(foo.bar) // prints 'bar'
console.log(foo.baz) // prints undefined

//
// interface with optional readonly property
//
interface iFoo {
    readonly bar: string;
    readonly baz?: string;
}

const ifoo:iFoo = {bar: "bar"}
console.log(ifoo.bar)  // prints 'bar'
console.log(ifoo.baz)  // prints undefined


//
// class implements bar as a getter, 
// but leaves off baz.
//
class iBarClass implements iFoo {

    get bar() { return "bar" }
}
const iBarInstance = new iBarClass()
console.log(iBarInstance.bar) // prints 'bar'
console.log(iBarInstance.baz) // prints 'undefined'
// accessing baz gives warning that baz does not exist 
// on iBarClass but returns undefined
// note that you could define baz as a getter
// and just return undefined to remove the warning.


//
// class implements optional readonly property as a getter
//
class iBazClass extends iBarClass {
    private readonly _baz?: string

    constructor(baz?:string) {
        super()
        this._baz = baz
    }

    get baz() { return this._baz; }
}

const iBazInstance = new iBazClass("baz")
console.log(iBazInstance.bar)  // prints bar
console.log(iBazInstance.baz)  // prints baz
Ezward
  • 17,327
  • 6
  • 24
  • 32
  • 73
    Nice answer. Also, note that, unlike in C#, properties are not currently virtualized in TypeScript (v0.9.5). When you implement "get bar()" in a derived class, you are replacing "get bar()" in the parent. Implications include not being able to call the base class accessor from the derived accessor. This is only true for properties - methods behave as you might expect. See answer by SteveFenton here: http://stackoverflow.com/questions/13121431/typescript-accessing-member-of-base-class – lightw8 Jan 17 '14 at 19:29
  • 17
    I'm slightly confused about the underscore. Typescript convention says not to use underscores for private variables? But in this case, we have to use underscores - or we'll get a conflict between the private and public "bar" – Kokodoko Apr 07 '16 at 10:43
  • 4
    Use the the underscore is a personal preference for private properties. However, I believe you are right in that we want the property to have a different name than the getter/setter methods. – Ezward Apr 07 '16 at 16:24
  • 3
    Why do you use `myFoo.bar = true` instead of `myFoo.bar(true);` or `myFoo.setBar(true);` ?? – Daniel W. Feb 21 '17 at 10:30
  • 9
    @DanFromGermany A property is "syntactic sugar" for a pair of "get" and "set" methods. Microsoft originated the concept of a property with Visual Basic and carried it over to .NET languages such as C# and VB.NET. For example, see [Properties (C# Programming Guide)](https://learn.microsoft.com/en-us/dotnet/articles/csharp/programming-guide/classes-and-structs/properties). Properties simplify accessing the state of an object and (in my opinion) eliminate the "noisiness" of having to deal with "get/set" method pairs. (Or sometimes only "get" methods where immutability is desired.) – DavidRR May 14 '17 at 14:53
  • 2
    Note that in this example you should just let the properties be public. In languages such as Java you do not want to do that since you want to reserve the right to add logic later in your getter/setter and if someone uses foo.x = 1 instead of foo.getX() you are screwed. But here the syntax for getter and setter is the same as for accessing public properties so you can always add logic later without breaking changes. – user2251745 Sep 20 '17 at 12:43
  • Is it possible to write these get and set functions with lambda-functions in arrow notation? @Ezward – Michael Chen Feb 12 '21 at 13:21
  • @MichaelChen You can use a lambda; `get: function () {return this._bar;},` is equivalent to `get:()=>this._bar,` – Ezward Mar 24 '21 at 20:05
  • @MichaelChen class syntax does now allow lambdas: see https://stackoverflow.com/questions/33827519/es6-getter-setter-with-arrow-function – Ezward Mar 24 '21 at 20:18
  • For the purposes of TS types, how can we set a `get` to be optional? I dropped `?`s pretty much everywhere, and just got syntax errors. – CodeFinity Jul 09 '21 at 22:47
  • 1
    @CodeFinity The 'optional' is a type concept. An optional string is the same as type = string|undefined. You can use an optional readonly property in your Type or Interface. A getter is an implementation that would return this type; you can implement that is several ways; I've added examples in the answer. – Ezward Jul 10 '21 at 16:57
  • ```TypeScript get fullName(): string { return `${this.firstName} ${this.lastName}`; } ``` How about a method like this? It's not itself a **property.** It uses two **required properties,** but is itself an optional **getter.** I already resolved the issue by just making it required and updating the data itself. Thinking about this now, it may not be wise to make a **getter** like this optional as we should be able to use `.fullName` consistently. – CodeFinity Jul 11 '21 at 10:00
  • Again, the return type can be an optional string, which is the similar to string|undefined, but the getter is an implementation; it may return a string OR undefined to conform to an optional string type. – Ezward Jul 11 '21 at 21:19
  • ES Lint won't allow this eslint.org/docs/rules/no-underscore-dangle – Sajith Mantharath Feb 07 '22 at 11:37
  • What are the benefits of using set and get? – Jun Nov 17 '22 at 19:25
  • Using get/set makes the code look that accesses the property look like a simple variable access, but underneath it is running a function. You can do logic to compute the property value for instance, you may cache the value so you don't have to run that logic twice, or you could write code to ensure the property was only ever set once and throws an error if you try to reset it. So it can be used to hide a bunch of implementation detail behind a simple looking property access. – Ezward Nov 18 '22 at 18:22
  • Can you explain what happens in an app targeting, say, es6? Asking here specifically about, "_However, in order to use it at all, you must make sure the TypeScript compiler targets ECMAScript5._" getters and setters certainly [seem to be current in the latest ECMAscript spec](https://tc39.es/ecma262/multipage/ecmascript-language-functions-and-classes.html#sec-method-definitions). – ruffin Jan 18 '23 at 19:53
  • Yes, the answer was written prior to ES6. I'll update it. – Ezward Jan 20 '23 at 00:46
154

Ezward has already provided a good answer, but I noticed that one of the comments asks how it is used. For people like me who stumble across this question, I thought it would be useful to have a link to the official documentation on getters and setters on the Typescript website as that explains it well, will hopefully always stay up-to-date as changes are made, and shows example usage:

http://www.typescriptlang.org/docs/handbook/classes.html

In particular, for those not familiar with it, note that you don't incorporate the word 'get' into a call to a getter (and similarly for setters):

var myBar = myFoo.getBar(); // wrong    
var myBar = myFoo.get('bar');  // wrong

You should simply do this:

var myBar = myFoo.bar;  // correct (get)
myFoo.bar = true;  // correct (set) (false is correct too obviously!)

given a class like:

class foo {
  private _bar:boolean = false;

  get bar():boolean {
    return this._bar;
  }
  set bar(theBar:boolean) {
    this._bar = theBar;
  }
}

then the 'bar' getter for the private '_bar' property will be called.

TornadoAli
  • 2,065
  • 2
  • 19
  • 13
  • 1
    If I was wanting to replace a public class-level var with a property, is it a straight drop-in replacement that I can put in place and not worry about it? In other words, if I regression test one accessor and one setter, can I deem it a success? Or are there cases where it won't work exactly the same as a var and i need to test all 100 places that use this var/prop? – Adam Plocher Nov 19 '18 at 11:51
  • 2
    I was wondering if there was a workaround for for using underscores to distinguish the property name from the getter or setter methods. In one course I was doing they said underscores were not preferred but didn't give an alternative. – cham May 19 '19 at 02:53
  • 3
    @cham You don't have to use underscores here... You can call the private variable notbar if you want. – Robert McKee Nov 04 '19 at 22:47
75

Here's a working example that should point you in the right direction:

class Foo {
    _name;

    get Name() {
        return this._name;
    }

    set Name(val) {
        this._name = val;
    }
}

Getters and setters in JavaScript are just normal functions. The setter is a function that takes a parameter whose value is the value being set.

sandrozbinden
  • 1,577
  • 1
  • 17
  • 28
Brian Terlson
  • 9,330
  • 1
  • 21
  • 18
9

You can write this

class Human {
    private firstName : string;
    private lastName : string;

    constructor (
        public FirstName?:string, 
        public LastName?:string) {

    }

    get FirstName() : string {
        console.log("Get FirstName : ", this.firstName);
        return this.firstName;
    }
    set FirstName(value : string) {
        console.log("Set FirstName : ", value);
        this.firstName = value;
    } 

    get LastName() : string {
        console.log("Get LastName : ", this.lastName);
        return this.lastName;
    }
    set LastName(value : string) {
        console.log("Set LastName : ", value);
        this.lastName = value;
    } 

}
k33g_org
  • 530
  • 5
  • 8
5

I think I probably get why is it so confusing. In your example, we wanted getters and setters for _name. But we achieve that by creating getters and setters for an unrelated class variable Name.

Consider this:

class Car {
    private tiresCount = 4;
    get yourCarTiresCount(){
        return this.tiresCount;
    }
    set yourCarTiresCount(count) {
        alert('You shouldn\'t change car tire count')
    }
}

Above code does following:

  1. get and set create getter and setter for yourCarTiresCount (not for tiresCount).

The getter is :

function () {
    return this.tiresCount;
}

and the setter is :

function (count) {
    alert('You shouldn\'t change car tire count');
}

Meaning, every time we do new Car().yourCarTiresCount, getter runs. And for every new Car().yourCarTiresCount('7') setter runs.

  1. Indirectly create getter, but not the setter, for private tireCount.
Nurutomo
  • 3
  • 3
dasfdsa
  • 7,102
  • 8
  • 51
  • 93
  • It's best practice to have the same name for a property because it's the private value you are making public indirectly i.e having a `_name:string` and accessors of `set name(value:string){this._name=value} ` and `get name(){return this._name}` make it clear what varible you're accessing and the method intentions – Felix Orinda Mar 26 '22 at 19:15
5

TS offers getters and setters which allow object properties to have more control of how they are accessed (getter) or updated (setter) outside of the object. Instead of directly accessing or updating the property a proxy function is called.

Example:

class Person {
    constructor(name: string) {
        this._name = name;
    }

    private _name: string;

    get name() {
        return this._name;
    }

    // first checks the length of the name and then updates the name.
    set name(name: string) {
        if (name.length > 10) {
            throw new Error("Name has a max length of 10");
        }

        this._name = name;  
    }

    doStuff () {
        this._name = 'foofooooooofoooo';
    }


}

const person = new Person('Willem');

// doesn't throw error, setter function not called within the object method when this._name is changed
person.doStuff();  

// throws error because setter is called and name is longer than 10 characters
person.name = 'barbarbarbarbarbar';  
Willem van der Veen
  • 33,665
  • 16
  • 190
  • 155
4

It is very similar to creating common methods, simply put the keyword reserved get or set at the beginning.

class Name{
    private _name: string;

    getMethod(): string{
        return this._name;
    }

    setMethod(value: string){
        this._name = value
    }

    get getMethod1(): string{
        return this._name;
    }

    set setMethod1(value: string){
        this._name = value
    }
}

class HelloWorld {

    public static main(){

        let test = new Name();

        test.setMethod('test.getMethod() --- need ()');
            console.log(test.getMethod());

        test.setMethod1 = 'test.getMethod1 --- no need (), and used = for set ';
            console.log(test.getMethod1);
    }
}
HelloWorld.main();

In this case you can skip return type in get getMethod1() {

    get getMethod1() {
        return this._name;
    }
Angel Angel
  • 19,670
  • 29
  • 79
  • 105
4

Based on example you show, you want to pass a data object and get a property of that object by get(). for this you need to use generic type, since data object is generic, can be any object.

export class Attributes<T> {
    constructor(private data: T) {}
    get = <K extends keyof T>(key: K): T[K] => {
      return this.data[key];
    };
    set = (update: T): void => {
      //   this is like spread operator. it will take this.data obj and will overwrite with the update obj
      // ins tsconfig.json change target to Es6 to be able to use Object.assign()
      Object.assign(this.data, update);
    };
    getAll(): T {
      return this.data;
    }
  }

< T > refers to generic type. let's initialize an instance

 const myAttributes=new Attributes({name:"something",age:32})

 myAttributes.get("name")="something"

Notice this syntax

<K extends keyof T>

in order to be able to use this we should be aware of 2 things:

1- in typestring strings can be a type.

2- all object properties in javascript essentially are strings.

when we use get(), type of argument that it is receiving is a property of object that passed to constructor and since object properties are strings and strings are allowed to be a type in typescript, we could use this <K extends keyof T>

Yilmaz
  • 35,338
  • 10
  • 157
  • 202
3

If you are looking for way to use get and set on any object (not a class) Proxy may be usefull: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy

const target = {
  message1: "hello",
  message2: "everyone"
};

const handler3 = {
  get: function (target, prop, receiver) {
    if (prop === "message2") {
      return "world";
    }
    return Reflect.get(...arguments);
  },
};

const proxy3 = new Proxy(target, handler3);

console.log(proxy3.message1); // hello
console.log(proxy3.message2); // world

Note: be aware that this is new api not supported and required polifill for older browsers

gdbdable
  • 4,445
  • 3
  • 30
  • 46
2

Below is an example how you can add getter & setter -

class Person {
    private _age: number;
    private _firstName: string;
    private _lastName: string;

 
    public get age() {
        return this._age;
    }

    public set age(theAge: number) {
        if (theAge <= 0 || theAge >= 200) {
            throw new Error('The age is invalid');
        }
        this._age = theAge;
    }

    public getFullName(): string {
        return `${this._firstName} ${this._lastName}`;
    }
}
0

Although TypeScript analyzes the initialization of the property, if you always want to handle this case yourself, you can set to false for this setting in ts.config.json.

{  
  "compilerOptions": {    
    "strict": true,
    "strictPropertyInitialization": false       
  }  
}

Strict Property Initialization - strictPropertyInitialization When set to true, TypeScript will raise an error when a class property was declared but not set in the constructor.

In this case, you should consider other cases too, which you will see in the links below.

class UserAccount {
  name: string;
  accountType = "user";
 
  email: string;//Property 'email' has no initializer and is not definitely assigned in the constructor.
  address: string | undefined;
 
  constructor(name: string) {
    this.name = name;
    // Note that this.email is not set
  }
}

this.name is set specifically.
this.accountType is set by default.
this.email is not set and raises an error.
this.address is declared as potentially undefined which means it does not have to be set.

The compiler doesn't raise an error if we set the strictPropertyInitialization to false

  private _name : string;
  public get name() : string {
    return this._name;
  }
  public set name(v : string) {
    this._name = v;
  }

https://www.typescriptlang.org/docs/handbook/2/classes.html#--strictpropertyinitialization https://www.typescriptlang.org/tsconfig#strictPropertyInitialization

gurkan
  • 884
  • 4
  • 16
  • 25
-5

If you are working with TypeScript modules and are trying to add a getter that is exported, you can do something like this:

// dataStore.ts
export const myData: string = undefined;  // just for typing support
let _myData: string;  // for memoizing the getter results

Object.defineProperty(this, "myData", {
    get: (): string => {
        if (_myData === undefined) {
            _myData = "my data";  // pretend this took a long time
        }

        return _myData;
    },
});

Then, in another file you have:

import * as dataStore from "./dataStore"
console.log(dataStore.myData); // "my data"
cjbarth
  • 4,189
  • 6
  • 43
  • 62
  • 8
    That's terrible advice. In particular, `this` must be undefined at the top level scope of a module. You could use `exports` instead but you should not do it at all as it is practically guaranteed to cause compatibility problems – Aluan Haddad Jan 12 '18 at 10:13