pobierz i ustaw w TypeScript

657

Próbuję utworzyć metodę get i set dla właściwości:

private _name: string;

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

Jakie jest słowo kluczowe, aby ustawić wartość?

MuriloKunze
źródło
12
Znak podkreślenia i PascalCase są w konflikcie z przewodnikami do kodowania maszynopisu
Niels Steenbeek
2
Cześć @NielsSteenbeek - postępując zgodnie ze wskazówkami dla autorów TypeScript z właściwościami i polami kopii zapasowej, może dojść do konfliktu nazw. Jakie jest sugerowane podejście?
Jordania
Być może: typescript private name: string; getName() { get: { return this.name; } set: { this.name = ???; } }
Jordan
7
Dobrze, że te wytyczne kodowania maszynopisu są dość nieatrakcyjne. Używałbym ich tylko pod przymusem (np. Zapłacono mi za to).
Thomas Eding
14
@NielsSteenbeek: czy przeczytałeś ten dokument? „To NIE jest nakazowa wytyczna dla społeczności TypeScript”
Jonathan Cast

Odpowiedzi:

1083

TypeScript używa składni getter / setter, która jest podobna do ActionScript3.

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

Spowoduje to wygenerowanie tego kodu JavaScript przy użyciu funkcji 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;
})();

Aby go użyć,

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

Aby jednak w ogóle z niego korzystać, należy się upewnić, że kompilator TypeScript jest ukierunkowany na ECMAScript5. Jeśli korzystasz z kompilatora wiersza poleceń, użyj --targetflagi w ten sposób;

tsc --target ES5

Jeśli używasz programu Visual Studio, musisz edytować plik projektu, aby dodać flagę do konfiguracji dla narzędzia do budowania TypeScriptCompile. Możesz to zobaczyć tutaj :

Jak @DanFromGermany sugeruje poniżej, jeśli po prostu czytasz i piszesz lokalną właściwość foo.bar = true, to posiadanie pary setera i gettera to przesada. Zawsze możesz je dodać później, jeśli chcesz coś zrobić, na przykład zalogować się, za każdym razem, gdy właściwość jest czytana lub zapisywana.

Ezward
źródło
59
Niezła odpowiedź. Zauważ też, że w przeciwieństwie do C #, właściwości nie są obecnie wirtualizowane w TypeScript (v0.9.5). Kiedy implementujesz „get bar ()” w klasie pochodnej, zastępujesz „get bar ()” w rodzicu. Implikacje obejmują niemożność wywołania akcesorium klasy podstawowej z akcesorium pochodnego. Dotyczy to tylko właściwości - metody zachowują się tak, jak można się spodziewać. Zobacz odpowiedź SteveFentona tutaj: stackoverflow.com/questions/13121431/...
David Cuccia
14
Jestem nieco zdezorientowany co do podkreślenia. Konwencja maszynopisu mówi, aby nie używać podkreślników dla zmiennych prywatnych? Ale w tym przypadku musimy użyć podkreślenia - w przeciwnym razie dojdzie do konfliktu między prywatnym a publicznym „barem”
Kokodoko,
4
Podkreślenie jest osobistą preferencją dla nieruchomości prywatnych. Uważam jednak, że masz rację, ponieważ chcemy, aby właściwość miała inną nazwę niż metody pobierające / ustawiające.
Ezward,
3
Dlaczego używasz myFoo.bar = truezamiast myFoo.bar(true);lub myFoo.setBar(true);??
Daniel W.
6
@DanFromGermany Właściwością jest „cukier składniowy” dla pary metod „get” i „set”. Microsoft stworzył koncepcję właściwości za pomocą Visual Basic i przeniósł ją na języki .NET, takie jak C # i VB.NET. Na przykład zobacz Właściwości (Podręcznik programowania w języku C #) . Właściwości upraszczają dostęp do stanu obiektu i (moim zdaniem) eliminują „hałas” związany z parami metod „get / set”. (Lub czasami tylko metody „get”, w których pożądana jest niezmienność.)
DavidRR
112

Ezward udzielił już dobrej odpowiedzi, ale zauważyłem, że jeden z komentarzy dotyczy tego, jak się go stosuje. Dla osób takich jak ja, którzy natkną się na to pytanie, pomyślałem, że przydatne byłoby posiadanie linku do oficjalnej dokumentacji na temat programów pobierających i ustawiających na stronie internetowej Typescript, ponieważ to dobrze to wyjaśnia, mam nadzieję, że zawsze będzie na bieżąco, ponieważ zmiany są wprowadzane wykonane i pokazuje przykładowe użycie:

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

W szczególności dla tych, którzy się z nim nie zaznajomili, zauważ, że nie włączasz słowa „get” do wywołania gettera (i podobnie dla seterów):

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

Powinieneś po prostu to zrobić:

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

biorąc udział w klasie takiej jak:

class foo {
  private _bar:boolean = false;

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

wtedy zostanie wywołany getter „bar” dla prywatnej właściwości „_bar”.

TornadoAli
źródło
Jeśli chciałem zastąpić publiczną zmienną klasy na właściwość, czy jest to zwykła zamiana, którą mogę wprowadzić i nie przejmować się nią? Innymi słowy, jeśli przetestuję regresję jednego akcesorium i jednego setera, czy mogę uznać to za sukces? Czy są przypadki, w których nie będzie działać dokładnie tak samo jak var i muszę przetestować wszystkie 100 miejsc, które używają tego var / prop?
Adam Plocher,
Zastanawiałem się, czy istnieje sposób obejścia problemu za pomocą podkreślników do odróżnienia nazwy właściwości od metod pobierających lub ustawiających. W jednym kursie mówiłem, że podkreślenia nie są preferowane, ale nie dają alternatywy.
cham
1
@cham Nie musisz tutaj używać podkreślników ... Możesz wywołać prywatną zmienną notbar, jeśli chcesz.
Robert McKee,
58

Oto działający przykład, który powinien wskazać właściwy kierunek:

class Foo {
    _name;

    get Name() {
        return this._name;
    }

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

Funkcje pobierające i ustawiające w JavaScript są zwykłymi funkcjami. Seter jest funkcją, która przyjmuje parametr, którego wartość jest ustawianą wartością.

Brian Terlson
źródło
30
Żeby było jasne, nie ma potrzeby posiadania właściwości, gettera i setera static.
Drew Noakes,
1
odniesienia do zmiennych są jednak nadal statyczne. Foo._namenależy zastąpićthis._name
Johannes,
6

Możesz to napisać

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
źródło
2
Dlaczego społeczeństwo w konstruktorze?
MuriloKunze,
17
Tak, nie może mieć publicznego w konstruktorze w tym kodzie. publicdefiniuje tutaj zduplikowanych członków.
orad
2
Możesz to napisać, ale się nie skompiluje
Justin
3

TS oferuje metody pobierające i ustawiające, które pozwalają właściwościom obiektu na większą kontrolę nad tym, jak są one uzyskiwane (getter) lub aktualizowane (ustawiające) poza obiektem. Zamiast bezpośredniego dostępu lub aktualizacji właściwości wywoływana jest funkcja proxy.

Przykład:

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
źródło
1

Jest bardzo podobny do tworzenia powszechnych metod, wystarczy umieścić słowo kluczowe zastrzeżone getlub setna początku.

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

W takim przypadku możesz pominąć wpisz return get getMethod1() {

    get getMethod1() {
        return this._name;
    }
Angel Angel
źródło
1

Myślę, że prawdopodobnie rozumiem, dlaczego to takie mylące. W twoim przykładzie chcieliśmy getterów i seterów _name. Ale osiągamy to, tworząc metody pobierające i ustawiające dla niepowiązanej zmiennej klasowej Name.

Rozważ to:

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

Powyższy kod wykonuje następujące czynności:

  1. geti settwórz getter i setter dla yourCarTiresCount( nie dlatiresCount ).

Getter to:

function() {
    return this.tiresCount ;
}

a seter jest:

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

To znaczy, za każdym razem, gdy to robimy new Car().yourCarTiresCount, getter biegnie. I dla każdego new Car().yourCarTiresCount('7')setera biegnie.

  1. Pośrednio utwórz gettera, ale nie setera, dla prywatnego tireCount.
dasfdsa
źródło
0

Jeśli szukasz sposobu na użycie get i set na dowolnym obiekcie (nie klasie), Proxy może być przydatne: 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

Uwaga: pamiętaj, że jest to nowy interfejs API nieobsługiwany i wymagany do wypełniania starszych przeglądarek

devi
źródło
-6

Jeśli pracujesz z modułami TypeScript i próbujesz dodać eksportowany moduł pobierający, możesz zrobić coś takiego:

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

Następnie w innym pliku masz:

import * as dataStore from "./dataStore"
console.log(dataStore.myData); // "my data"
cjbarth
źródło
8
To okropna rada. W szczególności thismusi być niezdefiniowany w zakresie najwyższego poziomu modułu. Możesz użyć exportszamiast tego, ale nie powinieneś tego robić, ponieważ praktycznie gwarantuje to problemy ze zgodnością
Aluan Haddad