получить и установить в TypeScript


660

Я пытаюсь создать метод get и set для свойства:

private _name: string;

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

Какое ключевое слово для установки значения?


12
Подчеркивание и PascalCase конфликтуют с
Нильс

2
Привет @NielsSteenbeek - следуя рекомендациям авторов TypeScript со свойствами и вспомогательными полями, вы столкнетесь с конфликтом имен. Какой предлагаемый подход?
Иордания

Возможно: typescript private name: string; getName() { get: { return this.name; } set: { this.name = ???; } }
Иордания

7
Хорошо, что эти правила кодирования Typescript довольно непривлекательны. Я бы использовал их только по принуждению (например, мне платили за это).
Томас

15
@NielsSteenbeek: вы читали этот документ? «Это НЕ предписывающее руководство для сообщества TypeScript»
Джонатан Каст

Ответы:


1084

TypeScript использует синтаксис getter / setter, который похож на ActionScript3.

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

Это создаст этот JavaScript, используя Object.defineProperty()функцию 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;
})();

Таким образом, чтобы использовать его,

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

Однако, чтобы использовать его вообще, вы должны убедиться, что компилятор TypeScript нацелен на ECMAScript5. Если вы используете компилятор командной строки, используйте --targetфлаг, подобный этому;

tsc --target ES5

Если вы используете Visual Studio, вы должны отредактировать файл проекта, чтобы добавить флаг в конфигурацию инструмента сборки TypeScriptCompile. Вы можете увидеть это здесь :

Как подсказывает @DanFromGermany ниже, если вы просто читаете и пишете локальное свойство, например foo.bar = true, наличие пары сеттеров и геттеров является излишним. Вы всегда можете добавить их позже, если вам нужно что-то сделать, например, войти в систему, когда свойство будет прочитано или записано.


59
Хороший ответ. Также обратите внимание, что, в отличие от C #, свойства в настоящее время не виртуализированы в TypeScript (v0.9.5). Когда вы реализуете «get bar ()» в производном классе, вы заменяете «get bar ()» в родительском. Последствия включают в себя невозможность вызова метода доступа базового класса из производного метода доступа. Это верно только для свойств - методы ведут себя так, как вы могли ожидать. Смотрите ответ от SteveFenton здесь: stackoverflow.com/questions/13121431/…
Дэвид Куччиа

14
Я немного запутался по поводу подчеркивания. Соглашение Typescript говорит не использовать подчеркивания для частных переменных? Но в этом случае мы должны использовать подчеркивание - или мы получим конфликт между частным и публичным «баром»
Kokodoko

4
Использование подчеркивания - это личное предпочтение частной собственности. Однако я считаю, что вы правы в том, что мы хотим, чтобы свойство имело другое имя, чем методы getter / setter.
Ezward

3
Почему вы используете myFoo.bar = trueвместо myFoo.bar(true);или myFoo.setBar(true);??
Даниэль В.

6
@DanFromGermany Свойство «синтаксический сахар» для пары методов «get» и «set». Microsoft создала концепцию свойства с помощью Visual Basic и перенесла его на языки .NET, такие как C # и VB.NET. Например, см. Свойства (Руководство по программированию в C #) . Свойства упрощают доступ к состоянию объекта и (на мой взгляд) устраняют «шумность» необходимости иметь дело с парами методов «получить / установить». (Или иногда только «получить» методы, где желательна неизменность.)
DavidRR

113

Эзвард уже дал хороший ответ, но я заметил, что один из комментариев спрашивает, как он используется. Для таких людей, как я, которые сталкиваются с этим вопросом, я подумал, что было бы полезно иметь ссылку на официальную документацию по получателям и установщикам на веб-сайте Typescript, поскольку это хорошо объясняет это, и мы надеемся, что они всегда будут в курсе происходящих изменений. сделал, и показывает пример использования:

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

В частности, для тех, кто не знаком с ним, обратите внимание, что вы не включаете слово «get» в вызов метода получения (и аналогично для метода установки):

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

Вы должны просто сделать это:

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

учитывая класс как:

class foo {
  private _bar:boolean = false;

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

тогда будет вызван метод 'bar' для частного свойства _bar.


Если я хотел заменить общедоступную переменную уровня класса свойством, это прямая замена, которую я могу поставить на место и не беспокоиться об этом? Другими словами, если я регрессивно тестирую один метод доступа и один метод установки, могу ли я считать его успешным? Или есть случаи, когда он не будет работать точно так же, как var, и мне нужно протестировать все 100 мест, которые используют этот var / prop?
Адам Плохер

Мне было интересно, есть ли обходной путь для использования подчеркивания, чтобы отличить имя свойства от методов получения или установки. В одном курсе, который я делал, они сказали, что подчеркивание не было предпочтением, но не давало альтернативы.
ч

1
@cham Здесь вам не нужно использовать подчеркивания ... Вы можете вызвать приватную переменную notbar, если хотите.
Роберт Макки

59

Вот рабочий пример, который должен указать вам правильное направление:

class Foo {
    _name;

    get Name() {
        return this._name;
    }

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

Методы получения и установки в JavaScript - это обычные функции. Сеттер - это функция, которая принимает параметр, значением которого является устанавливаемое значение.


30
Чтобы было ясно, нет необходимости в свойствах, получателях и установщиках static.
Дрю Ноакс

1
ссылки на переменные все еще статичны. Foo._nameследует заменить наthis._name
Йоханнес

6

Вы можете написать это

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

}

2
Почему публика в конструкторе?
МурилоКунце

17
Да, в этом коде не может быть public в конструкторе. publicздесь определяются дубликаты членов.
Орад

2
Вы можете написать это, но это не скомпилируется
Джастин

3

TS предлагает методы получения и установки, которые позволяют свойствам объекта иметь больший контроль над тем, как к ним получают доступ (средство получения) или обновляют (средство установки) вне объекта. Вместо прямого доступа или обновления свойства вызывается прокси-функция.

Пример:

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';  

1

Это очень похоже на создание общих методов, просто зарезервируйте ключевое слово getили setв начале.

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

В этом случае вы можете пропустить тип возврата в get getMethod1() {

    get getMethod1() {
        return this._name;
    }

1

Я думаю, я, наверное, понимаю, почему это так запутанно. В вашем примере мы хотели получить и установить для _name. Но мы достигаем этого, создавая методы получения и установки для несвязанной переменной класса Name.

Учти это:

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

Выше код делает следующее:

  1. getи setсоздать геттер и сеттер для yourCarTiresCount( не дляtiresCount ).

Геттер это:

function() {
    return this.tiresCount ;
}

и сеттер это:

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

Это значит, что каждый раз, когда мы это делаем new Car().yourCarTiresCount, работает геттер. И на каждый new Car().yourCarTiresCount('7')сеттер работает.

  1. Косвенно создайте геттер, но не сеттер, для частного tireCount.

0

Если вы ищете способ использовать get и set для любого объекта (не для класса), это Proxy может быть полезно: 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

Примечание: имейте в виду, что это новый API-интерфейс не поддерживается и требуется полифилл для старых браузеров


-6

Если вы работаете с модулями TypeScript и пытаетесь добавить экспортируемый метод получения, вы можете сделать что-то вроде этого:

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

Затем в другом файле у вас есть:

import * as dataStore from "./dataStore"
console.log(dataStore.myData); // "my data"

8
Это ужасный совет. В частности, thisдолжно быть неопределенным в области видимости верхнего уровня модуля. Вы могли бы использовать exportsвместо этого, но вы не должны делать это вообще, поскольку это практически гарантированно вызовет проблемы совместимости
Aluan Haddad
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.