obter e definir no TypeScript

660

Estou tentando criar método get e set para uma propriedade:

private _name: string;

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

Qual é a palavra-chave para definir um valor?

MuriloKunze
fonte
12
Os conflitos de sublinhado e PascalCase com os guidlines codificação Dactilografado: github.com/Microsoft/TypeScript/wiki/Coding-guidelines
Niels Steenbeek
2
Olá @NielsSteenbeek - seguindo as diretrizes dos contribuidores do TypeScript com propriedades e campos de apoio, você pode ter um conflito de nome. Qual é a abordagem sugerida?
Jordan
Talvez: typescript private name: string; getName() { get: { return this.name; } set: { this.name = ???; } }
Jordânia
7
Ainda bem que essas diretrizes de codificação Typecript são bastante pouco atraentes. Eu os usaria apenas sob coerção (por exemplo, fui pago para fazê-lo).
Thomas Eding
15
@NielsSteenbeek: você leu esse documento? "Esta NÃO é uma diretriz prescritiva para a comunidade TypeScript"
Jonathan Cast

Respostas:

1084

O TypeScript usa a sintaxe getter / setter, semelhante ao ActionScript3.

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

Isso produzirá esse JavaScript, usando o Object.defineProperty()recurso ECMAScript 5 .

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

Então, para usá-lo,

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

No entanto, para usá-lo, verifique se o compilador TypeScript tem como alvo o ECMAScript5. Se você estiver executando o compilador de linha de comando, use --targetsinalizador como este;

tsc --target ES5

Se você estiver usando o Visual Studio, edite o arquivo do projeto para adicionar o sinalizador à configuração da ferramenta de construção TypeScriptCompile. Você pode ver isso aqui :

Como o @DanFromGermany sugere abaixo, se você está simplesmente lendo e escrevendo uma propriedade local como foo.bar = true, ter um par de setter e getter é um exagero. Você sempre pode adicioná-los mais tarde se precisar fazer algo, como log, sempre que a propriedade for lida ou gravada.

Ezward
fonte
59
Boa resposta. Além disso, observe que, diferentemente do C #, atualmente as propriedades não são virtualizadas no TypeScript (v0.9.5). Ao implementar "get bar ()" em uma classe derivada, você substitui "get bar ()" no pai. As implicações incluem não poder chamar o acessador de classe base do acessador derivado. Isso é verdade apenas para propriedades - os métodos se comportam como você poderia esperar. Veja a resposta de SteveFenton aqui: stackoverflow.com/questions/13121431/…
David Cuccia
14
Estou um pouco confuso sobre o sublinhado. A convenção datilografada diz para não usar sublinhados para variáveis ​​privadas? Mas, neste caso, temos que sublinhados uso - ou teremos um conflito entre o "bar" privado e público
Kokodoko
4
Use o sublinhado é uma preferência pessoal para propriedades particulares. No entanto, acredito que você está certo, pois queremos que a propriedade tenha um nome diferente dos métodos getter / setter.
Ezward
3
Por que você usa em myFoo.bar = truevez de myFoo.bar(true);ou myFoo.setBar(true);?
Daniel W.
6
@DanFromGermany Uma propriedade é "açúcar sintático" para um par de métodos "get" e "set". A Microsoft originou o conceito de uma propriedade com o Visual Basic e a transportou para linguagens .NET como C # e VB.NET. Por exemplo, consulte Propriedades (Guia de Programação em C #) . As propriedades simplificam o acesso ao estado de um objeto e (na minha opinião) eliminam o "barulho" de ter que lidar com pares de métodos "get / set". (Ou às vezes apenas "se" métodos em que se deseja a imutabilidade.)
DavidRR
113

Ezward já forneceu uma boa resposta, mas notei que um dos comentários pergunta como é usado. Para pessoas como eu que se deparam com essa questão, pensei que seria útil ter um link para a documentação oficial sobre getters e setters no site da Typecript, pois isso explica bem, espero que sempre fique atualizado quando houver mudanças. made e mostra um exemplo de uso:

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

Em particular, para aqueles que não estão familiarizados com isso, observe que você não incorpora a palavra 'get' em uma chamada para um getter (e da mesma forma para os setters):

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

Você deve simplesmente fazer o seguinte:

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

dada uma aula como:

class foo {
  private _bar:boolean = false;

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

então, o getter 'bar' da propriedade privada '_bar' será chamado.

TornadoAli
fonte
Se eu estava querendo substituir uma var de nível de classe pública por uma propriedade, é uma substituição direta que eu posso colocar em prática e não me preocupar com isso? Em outras palavras, se eu testar a regressão um acessador e um levantador, posso considerá-lo um sucesso? Ou há casos em que não funcionará exatamente o mesmo que um var e eu preciso testar todos os 100 locais que usam esse var / prop?
Adam Plocher
Eu queria saber se havia uma solução alternativa para o uso de sublinhados para distinguir o nome da propriedade dos métodos getter ou setter. Em um curso que eu estava fazendo, eles disseram que os sublinhados não eram os preferidos, mas não deram uma alternativa.
Cham
1
@cham Você não precisa usar sublinhados aqui ... Você pode chamar a variável privada notbar, se quiser.
Robert McKee
59

Aqui está um exemplo prático que deve apontar você na direção certa:

class Foo {
    _name;

    get Name() {
        return this._name;
    }

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

Getters e setters em JavaScript são apenas funções normais. O setter é uma função que aceita um parâmetro cujo valor é o valor que está sendo definido.

Brian Terlson
fonte
30
Para ser claro, não há necessidade de propriedade, getter e setter static.
Drew Noakes
1
as referências de variáveis ​​ainda são estáticas. Foo._namedeve ser substituído porthis._name
Johannes
6

Você pode escrever isso

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
fonte
2
Por que o público em construtor?
MuriloKunze
17
Sim, não pode ter público no construtor neste código. publicaqui define membros duplicados.
orad 27/07/2013
2
Você pode escrevê-lo, mas ele não vai compilar
Justin
3

O TS oferece getters e setters que permitem que as propriedades do objeto tenham mais controle de como elas são acessadas (getter) ou atualizadas (setter) fora do objeto. Em vez de acessar ou atualizar diretamente a propriedade, uma função de proxy é chamada.

Exemplo:

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

É muito semelhante à criação de métodos comuns, basta colocar a palavra-chave reservada getou setno início.

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

Nesse caso, você pode pular o tipo de retorno get getMethod1() {

    get getMethod1() {
        return this._name;
    }
Anjo anjo
fonte
1

Eu acho que provavelmente entendo porque é tão confuso. No seu exemplo, queríamos getters e setters para _name. Mas conseguimos isso criando getters e setters para uma variável de classe não relacionada Name.

Considere isto:

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

O código acima faz o seguinte:

  1. gete setcrie getter e setter para yourCarTiresCount( não paratiresCount ).

O getter é:

function() {
    return this.tiresCount ;
}

e o levantador é:

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

Significado, toda vez que fazemos new Car().yourCarTiresCount, getter é executado. E para cada new Car().yourCarTiresCount('7')setter é executado.

  1. Crie indiretamente getter, mas não o setter, para particular tireCount.
dasfdsa
fonte
0

Se você está procurando uma maneira de usar get e set em qualquer objeto (não uma classe), Proxy pode 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: lembre-se de que esta nova API não é suportada e é necessário polifill para navegadores antigos

devi
fonte
-6

Se você estiver trabalhando com módulos TypeScript e tentando adicionar um getter que é exportado, é possível fazer algo assim:

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

Em outro arquivo, você tem:

import * as dataStore from "./dataStore"
console.log(dataStore.myData); // "my data"
cjbarth
fonte
8
Esse é um conselho terrível. Em particular, thisdeve ser indefinido no escopo de nível superior de um módulo. Você poderia usar exportsem vez, mas você não deve fazê-lo em tudo, pois está praticamente garantido para problemas de compatibilidade de causa
Aluan Haddad