obtener y establecer en TypeScript

660

Estoy tratando de crear el método get y set para una propiedad:

private _name: string;

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

¿Cuál es la palabra clave para establecer un valor?

MuriloKunze
fuente
12
El guión bajo y PascalCase entran en conflicto con las pautas de codificación de Typecript
Niels Steenbeek
2
Hola @NielsSteenbeek: siguiendo las pautas de los contribuyentes de TypeScript con propiedades y campos de respaldo, terminarás con un conflicto de nombres. ¿Cuál es el enfoque sugerido?
Jordan
Quizás: typescript private name: string; getName() { get: { return this.name; } set: { this.name = ???; } }
Jordan
77
Lo bueno es que esas pautas de codificación mecanografiada son bastante poco atractivas. Solo los usaría bajo coacción (por ejemplo, me pagaron por hacerlo).
Thomas Eding
15
@NielsSteenbeek: ¿leíste ese documento? "Esta NO es una guía prescriptiva para la comunidad TypeScript"
Jonathan Cast el

Respuestas:

1084

TypeScript utiliza la sintaxis getter / setter que es como ActionScript3.

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

Eso producirá este JavaScript, utilizando la función ECMAScript 5 Object.defineProperty().

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

Para usarlo,

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

Sin embargo, para poder usarlo, debe asegurarse de que el compilador TypeScript esté dirigido a ECMAScript5. Si está ejecutando el compilador de línea de comando, use el --targetindicador como este;

tsc --target ES5

Si está utilizando Visual Studio, debe editar su archivo de proyecto para agregar el indicador a la configuración de la herramienta de compilación TypeScriptCompile. Puedes ver eso aquí :

Como @DanFromGermany sugiere a continuación, si simplemente está leyendo y escribiendo una propiedad local como foo.bar = true, tener un setter y getter pair es excesivo. Siempre puede agregarlos más adelante si necesita hacer algo, como iniciar sesión, cada vez que la propiedad se lee o se escribe.

Ezward
fuente
59
Buena respuesta. Además, tenga en cuenta que, a diferencia de C #, las propiedades no están virtualizadas actualmente en TypeScript (v0.9.5). Cuando implementa "get bar ()" en una clase derivada, está reemplazando "get bar ()" en el padre. Las implicaciones incluyen no poder llamar al descriptor de acceso de clase base desde el descriptor de acceso derivado. Esto solo es cierto para las propiedades: los métodos se comportan como cabría esperar. Vea la respuesta de SteveFenton aquí: stackoverflow.com/questions/13121431/…
David Cuccia
14
Estoy un poco confundido sobre el guión bajo. ¿La convención mecanografiada dice no usar guiones bajos para las variables privadas? Pero en este caso, tenemos que usar guiones bajos, o tendremos un conflicto entre el "bar" privado y público
Kokodoko
44
Usar el guión bajo es una preferencia personal para propiedades privadas. Sin embargo, creo que tiene razón en que queremos que la propiedad tenga un nombre diferente al de los métodos getter / setter.
Ezward
3
¿Por qué usas en myFoo.bar = truelugar de myFoo.bar(true);o myFoo.setBar(true);??
Daniel W.
66
@DanFromGermany Una propiedad es "azúcar sintáctico" para un par de métodos "get" y "set". Microsoft originó el concepto de una propiedad con Visual Basic y lo transfirió a lenguajes .NET como C # y VB.NET. Por ejemplo, consulte Propiedades (Guía de programación de C #) . Las propiedades simplifican el acceso al estado de un objeto y (en mi opinión) eliminan el "ruido" de tener que lidiar con los pares de métodos "get / set". (O a veces solo "obtener" métodos donde se desea la inmutabilidad.)
DavidRR
113

Ezward ya ha proporcionado una buena respuesta, pero noté que uno de los comentarios pregunta cómo se usa. Para las personas como yo que tropiezan con esta pregunta, pensé que sería útil tener un enlace a la documentación oficial sobre getters y setters en el sitio web de Typecript, ya que eso lo explica bien, con suerte siempre estaré actualizado ya que los cambios son hecho, y muestra el uso de ejemplo:

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

En particular, para aquellos que no están familiarizados con él, tenga en cuenta que no incorpora la palabra 'get' en una llamada a un getter (y de manera similar para los setters):

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

Simplemente debes hacer esto:

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

dado una clase como:

class foo {
  private _bar:boolean = false;

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

entonces se llamará al captador 'bar' para la propiedad privada '_bar'.

TornadoAli
fuente
Si quisiera reemplazar una variable de nivel de clase pública con una propiedad, ¿es un reemplazo directo que puedo implementar y no preocuparme? En otras palabras, si pruebo un regresor y un setter, ¿puedo considerarlo un éxito? ¿O hay casos en los que no funcionará exactamente igual que un var y necesito probar los 100 lugares que usan este var / prop?
Adam Plocher
Me preguntaba si había una solución alternativa para usar guiones bajos para distinguir el nombre de la propiedad de los métodos getter o setter. En un curso que estaba haciendo, dijeron que los guiones bajos no eran preferidos pero no daban una alternativa.
cham
1
@cham No tiene que usar guiones bajos aquí ... Puede llamar a la variable privada notbar si lo desea.
Robert McKee
59

Aquí hay un ejemplo de trabajo que debería orientarte en la dirección correcta:

class Foo {
    _name;

    get Name() {
        return this._name;
    }

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

Getters y setters en JavaScript son solo funciones normales. El setter es una función que toma un parámetro cuyo valor es el valor que se está configurando.

Brian Terlson
fuente
30
Para ser claros, no hay necesidad de que la propiedad, getter y setter sean static.
Drew Noakes
1
Sin embargo, las referencias variables siguen siendo estáticas. Foo._namedebería ser reemplazado conthis._name
Johannes
6

Puedes escribir esto

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
fuente
2
¿Por qué el público en constructor?
MuriloKunze
17
Sí, no puedo tener public en constructor en este código. publicaquí define miembros duplicados.
orad
2
Puedes escribirlo pero no se compilará
Justin
3

TS ofrece getters y setters que permiten que las propiedades de los objetos tengan más control sobre cómo se accede (getter) o cómo se actualizan (setter) fuera del objeto. En lugar de acceder o actualizar directamente la propiedad, se llama una función proxy.

Ejemplo:

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
fuente
1

Es muy similar a crear métodos comunes, simplemente ponga la palabra clave reservada geto setal principio.

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

En este caso, puede omitir el tipo de retorno en get getMethod1() {

    get getMethod1() {
        return this._name;
    }
Ángel Ángel
fuente
1

Creo que probablemente entiendo por qué es tan confuso. En su ejemplo, queríamos getters y setters para _name. Pero lo logramos creando captadores y establecedores para una variable de clase no relacionada Name.

Considera esto:

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

El código anterior hace lo siguiente:

  1. gety setcrea getter y setter para yourCarTiresCount( no paratiresCount ).

El captador es:

function() {
    return this.tiresCount ;
}

y el setter es:

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

Es decir, cada vez que lo hacemos new Car().yourCarTiresCount, getter corre. Y por cada new Car().yourCarTiresCount('7')setter corre.

  1. Crear indirectamente getter, pero no el setter, para privado tireCount.
dasfdsa
fuente
0

Si está buscando la manera de usar get y set en cualquier objeto (no una clase) Proxy puede ser útil: 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

Nota: tenga en cuenta que esta nueva API no es compatible y es necesaria para rellenar los navegadores antiguos

devi
fuente
-6

Si está trabajando con módulos TypeScript e intenta agregar un captador que se exporta, puede hacer algo como esto:

// 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;
    },
});

Luego, en otro archivo tienes:

import * as dataStore from "./dataStore"
console.log(dataStore.myData); // "my data"
cjbarth
fuente
8
Ese es un consejo terrible. En particular, thisdebe estar indefinido en el ámbito de nivel superior de un módulo. Podría usarlo exportsen su lugar, pero no debería hacerlo en absoluto, ya que está prácticamente garantizado que causará problemas de compatibilidad
Aluan Haddad