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?
typescript private name: string; getName() { get: { return this.name; } set: { this.name = ???; } }
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;
}
}
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;
})();
So to use it,
var myFoo = new foo();
if(myFoo.bar) { // calls the getter
myFoo.bar = false; // calls the setter and passes false
}
However, in order to use it at all, you must make sure the TypeScript compiler targets ECMAScript5. 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:
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 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.
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.
static
.
Foo._name
should be replaced with this._name
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;
}
}
public
here defines duplicate members.
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';
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>
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:
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.
Indirectly create getter, but not the setter, for private tireCount.
_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
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;
}
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
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}`;
}
}
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
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"
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
Success story sharing
myFoo.bar = true
instead ofmyFoo.bar(true);
ormyFoo.setBar(true);
??