get and set in TypeScript
Asked Answered
O

12

943

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?

Ollie answered 10/10, 2012 at 19:52 Comment(5)
The underscore and PascalCase conflicts with the Typescript coding guidlines: github.com/Microsoft/TypeScript/wiki/Coding-guidelinesEndoskeleton
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?Dorotea
Perhaps: typescript private name: string; getName() { get: { return this.name; } set: { this.name = ???; } } Dorotea
Good thing those Typescript coding guidelines are pretty unattractive. I would only use them under coercion (e.g. I was paid to do so).Villanueva
@NielsSteenbeek: did you read that document? "This is NOT a prescriptive guideline for the TypeScript community"Orban
F
1465

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 you 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
Fadden answered 12/10, 2012 at 0:19 Comment(18)
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: #13121931Adai
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"V
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.Fadden
Why do you use myFoo.bar = true instead of myFoo.bar(true); or myFoo.setBar(true); ??Tericaterina
@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). 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.)Rumrunner
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.Avlona
Is it possible to write these get and set functions with lambda-functions in arrow notation? @FaddenCorniculate
@MichaelChen You can use a lambda; get: function () {return this._bar;}, is equivalent to get:()=>this._bar,Fadden
@MichaelChen class syntax does now allow lambdas: see #33828019Fadden
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.Ehrlich
@Ehrlich 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.Fadden
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.Ehrlich
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.Fadden
ES Lint won't allow this eslint.org/docs/rules/no-underscore-dangleInodorous
What are the benefits of using set and get?Pharmacognosy
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.Fadden
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. 😕Taproom
Yes, the answer was written prior to ES6. I'll update it.Fadden
T
157

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.

Tomasatomasina answered 15/1, 2016 at 12:53 Comment(3)
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?Ceporah
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.Verge
@Verge You don't have to use underscores here... You can call the private variable notbar if you want.Taejon
S
76

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.

Selfexamination answered 10/10, 2012 at 20:7 Comment(2)
To be clear, there's no need for the property, getter and setter to be static.Ought
the variable references are still static though. Foo._name should be replaced with this._nameNudicaul
A
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;
    } 

}
Asinine answered 11/10, 2012 at 18:32 Comment(5)
Why the public in constructor?Ollie
Yes, can't have public in constructor in this code. public here defines duplicate members.Passageway
You can write it but it wont compilePauly
Is It ok to use Pascal Case in properties like get and set? can you provide resources or docs that enhance this practice?Now
It didnt work for me, capital case in set and get , it doesnot recognize.Maladjustment
H
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.
Habilitate answered 13/10, 2017 at 19:11 Comment(1)
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 intentionsManuel
C
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';  
Choice answered 11/7, 2019 at 14:53 Comment(0)
M
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;
    }
Mimi answered 22/3, 2016 at 14:13 Comment(0)
E
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>

Elissaelita answered 28/10, 2020 at 18:37 Comment(0)
R
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

Roaster answered 31/5, 2020 at 10:42 Comment(0)
R
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}`;
    }
}
Radii answered 3/9, 2021 at 9:25 Comment(0)
F
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

Fructificative answered 19/7, 2022 at 11:37 Comment(0)
A
-6

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"
Acie answered 28/12, 2017 at 18:34 Comment(1)
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 problemsPenelope

© 2022 - 2024 — McMap. All rights reserved.