dapatkan dan atur dalam TypeScript

660

Saya mencoba membuat get dan set metode untuk properti:

private _name: string;

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

Apa kata kunci untuk menetapkan nilai?

MuriloKunze
sumber
12
Garis bawah dan PascalCase bertentangan dengan pedoman penulisan kode Script
Niels Steenbeek
2
Hai @NielsSteenbeek - mengikuti panduan kontributor TypeScript dengan properti dan bidang dukungan yang Anda akan berakhir dengan konflik nama. Apa pendekatan yang disarankan?
Jordan
Mungkin: typescript private name: string; getName() { get: { return this.name; } set: { this.name = ???; } }
Jordan
7
Untunglah pedoman pengodean naskah itu tidak menarik. Saya hanya akan menggunakannya di bawah paksaan (misalnya saya dibayar untuk melakukannya).
Thomas Eding
15
@NielsSteenbeek: apakah Anda membaca dokumen itu? "Ini BUKAN pedoman preskriptif untuk komunitas TypeScript"
Jonathan Cast

Jawaban:

1084

TypeScript menggunakan sintaks pengambil / penyetel yang seperti ActionScript3.

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

Itu akan menghasilkan JavaScript ini, menggunakan fitur 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;
})();

Jadi untuk menggunakannya,

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

Namun, untuk menggunakannya sama sekali, Anda harus memastikan compiler TypeScript menargetkan ECMAScript5. Jika Anda menjalankan kompiler baris perintah, gunakan --targetflag seperti ini;

tsc --target ES5

Jika Anda menggunakan Visual Studio, Anda harus mengedit file proyek Anda untuk menambahkan bendera ke konfigurasi untuk alat membangun TypeScriptCompile. Anda dapat melihatnya di sini :

Seperti yang disarankan @DanFromGermany di bawah ini, jika Anda hanya membaca dan menulis properti lokal foo.bar = true, maka memiliki pasangan penyetel dan pengambil adalah berlebihan. Anda selalu dapat menambahkannya nanti jika Anda perlu melakukan sesuatu, seperti mencatat, kapan pun properti dibaca atau ditulis.

Ezward
sumber
59
Jawaban bagus. Juga, perhatikan bahwa, tidak seperti dalam C #, properti saat ini tidak tervirtualisasi dalam TypeScript (v0.9.5). Saat Anda menerapkan "get bar ()" di kelas turunan, Anda mengganti "get bar ()" di induknya. Implikasinya termasuk tidak dapat memanggil accessor kelas dasar dari accessor yang diturunkan. Ini hanya berlaku untuk properti - metode berperilaku seperti yang Anda harapkan. Lihat jawaban oleh SteveFenton di sini: stackoverflow.com/questions/13121431/…
David Cuccia
14
Saya sedikit bingung tentang garis bawah. Konvensi naskah mengatakan tidak menggunakan garis bawah untuk variabel pribadi? Tetapi dalam kasus ini, kita harus menggunakan garis bawah - atau kita akan mendapat konflik antara "bar" pribadi dan publik
Kokodoko
4
Gunakan garis bawah adalah preferensi pribadi untuk properti pribadi. Namun, saya yakin Anda benar karena kami ingin properti memiliki nama yang berbeda dari metode pengambil / penyetel.
Ezward
3
Mengapa Anda menggunakan myFoo.bar = truebukan myFoo.bar(true);atau myFoo.setBar(true);??
Daniel W.
6
@DanFromGermany Properti adalah "gula sintaksis" untuk sepasang metode "get" dan "set". Microsoft memulai konsep properti dengan Visual Basic dan membawanya ke bahasa .NET seperti C # dan VB.NET. Misalnya, lihat Properti (Panduan Pemrograman C #) . Properti menyederhanakan mengakses keadaan suatu objek dan (menurut saya) menghilangkan "keributan" karena harus berurusan dengan pasangan metode "get / set". (Atau kadang-kadang hanya metode "dapatkan" di mana ketidaksempurnaan diinginkan.)
DavidRR
113

Ezward telah memberikan jawaban yang baik, tetapi saya perhatikan bahwa salah satu komentar bertanya bagaimana itu digunakan. Bagi orang-orang seperti saya yang menemukan pertanyaan ini, saya pikir akan bermanfaat untuk memiliki tautan ke dokumentasi resmi tentang getter dan setter di situs web Script karena menjelaskannya dengan baik, mudah-mudahan akan selalu tetap up-to-date karena perubahan dibuat, dan menunjukkan contoh penggunaan:

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

Khususnya, bagi mereka yang tidak terbiasa dengan itu, perhatikan bahwa Anda tidak memasukkan kata 'masuk' ke dalam panggilan ke seorang pengambil (dan juga untuk setter):

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

Anda cukup melakukan ini:

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

diberi kelas seperti:

class foo {
  private _bar:boolean = false;

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

maka pengambil 'bar' untuk properti '_bar' pribadi akan dipanggil.

TornadoAli
sumber
Jika saya ingin mengganti var tingkat kelas publik dengan properti, apakah itu pengganti drop-in lurus yang bisa saya tempatkan dan tidak khawatir tentang hal itu? Dengan kata lain, jika saya menguji satu accessor dan satu setter, dapatkah saya menganggapnya sukses? Atau ada kasus di mana itu tidak akan berfungsi sama persis dengan var dan saya perlu menguji semua 100 tempat yang menggunakan var / prop ini?
Adam Plocher
Saya bertanya-tanya apakah ada solusi untuk menggunakan garis bawah untuk membedakan nama properti dari metode pengambil atau penyetel. Dalam satu saja yang saya lakukan mereka mengatakan garis bawah tidak disukai tetapi tidak memberikan alternatif.
cham
1
@cham Anda tidak perlu menggunakan garis bawah di sini ... Anda dapat memanggil notbar variabel pribadi jika mau.
Robert McKee
59

Berikut ini contoh yang berfungsi yang seharusnya mengarahkan Anda ke arah yang benar:

class Foo {
    _name;

    get Name() {
        return this._name;
    }

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

Getters dan setter dalam JavaScript hanyalah fungsi normal. Setter adalah fungsi yang mengambil parameter yang nilainya adalah nilai yang ditetapkan.

Brian Terlson
sumber
30
Untuk menjadi jelas, tidak perlu untuk properti, pengambil dan penyetel untuk menjadi static.
Drew Noakes
1
referensi variabel masih statis. Foo._nameharus diganti denganthis._name
Johannes
6

Anda bisa menulis ini

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
sumber
2
Mengapa publik di konstruktor?
MuriloKunze
17
Ya, tidak dapat memiliki publik dalam konstruktor dalam kode ini. publicdi sini mendefinisikan anggota duplikat.
orad
2
Anda dapat menulisnya tetapi tidak dapat dikompilasi
Justin
3

TS menawarkan getter dan setter yang memungkinkan properti objek memiliki kontrol lebih besar tentang bagaimana mereka diakses (pengambil) atau diperbarui (setter) di luar objek. Alih-alih secara langsung mengakses atau memperbarui properti, fungsi proxy dipanggil.

Contoh:

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

Ini sangat mirip dengan membuat metode umum, cukup cantumkan kata kunci getatau setdi awal.

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

Dalam hal ini Anda dapat melewati ketik kembali get getMethod1() {

    get getMethod1() {
        return this._name;
    }
Malaikat Malaikat
sumber
1

Saya pikir saya mungkin mengerti mengapa itu sangat membingungkan. Dalam contoh Anda, kami menginginkan getter dan setter _name. Tapi kami mencapainya dengan membuat getter dan setter untuk variabel kelas yang tidak terkait Name.

Pertimbangkan ini:

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

Kode di atas tidak mengikuti:

  1. getdan setbuat getter dan setter untuk yourCarTiresCount( bukan untuktiresCount ).

Pembuatnya adalah:

function() {
    return this.tiresCount ;
}

dan penyetelnya adalah:

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

Artinya, setiap kali kita melakukannya new Car().yourCarTiresCount, rajin giat berlari. Dan untuk setiap new Car().yourCarTiresCount('7')setter berjalan.

  1. Secara tidak langsung membuat pengambil, bukan penyetel, untuk pribadi tireCount.
dasfdsa
sumber
0

Jika Anda mencari cara untuk menggunakan get dan set pada objek apa pun (bukan kelas) Proxy mungkin berguna: 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

Catatan: perlu diketahui bahwa ini adalah api baru yang tidak didukung dan diperlukan polifill untuk browser lama

devi
sumber
-6

Jika Anda bekerja dengan modul TypeScript dan mencoba menambahkan pengambil yang diekspor, Anda dapat melakukan sesuatu seperti ini:

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

Kemudian, di file lain yang Anda miliki:

import * as dataStore from "./dataStore"
console.log(dataStore.myData); // "my data"
cjbarth
sumber
8
Itu saran yang mengerikan. Khususnya, thisharus tidak ditentukan pada lingkup tingkat atas modul. Anda dapat menggunakannya exportssebagai gantinya tetapi Anda tidak boleh melakukannya sama sekali karena secara praktis dijamin akan menyebabkan masalah kompatibilitas
Aluan Haddad