Typescript: Различные модификаторы доступа для геттера и сеттера

Созданный на 21 апр. 2015  ·  40Комментарии  ·  Источник: microsoft/TypeScript

Можно ли реализовать разные модификаторы доступа для геттера и сеттера? Таким образом, сеттер может быть, например, закрытым/защищенным, а геттер общедоступным. В некоторых случаях это действительно полезно, когда значение должно быть доступно только для чтения.

Пример:

class MyClass {
    private _myProp: any;
    private set myProp(value: any) {
        this._myProp = value;
    }
    public get myProp(): any {
        return this._myProp;
    }
}
Declined Suggestion Too Complex

Самый полезный комментарий

В любом случае добавлю мой +1 за это, на случай, если это будет рассмотрено снова в будущем...

Все 40 Комментарий

12

Поскольку № 12 закрыт с № 6532, теперь кажется, что эта проблема выходит за рамки.
Есть ли у вас какие-либо планы по внедрению различных модификаторов доступа?
Потому что readonly недостаточно для решения того, что здесь описано, поскольку свойство может быть действительно доступным для записи внутри класса.

Я считаю, что мне также следует перенести сюда мой пример из связанной проблемы:

declare abstract class Emitter {
    new (): Emitter;
    on: (name:string, handler: (arg:any) => void) => void;
    off: (name:string, handler: (arg:any) => void) => void;
    protected emit: (name:string, arg:any) => void;
}

class Person extends Emitter {
    constructor(name:string) {
        super();
        this.name = name;
    }

    private _name:string;
    get name() {
        return this._name;
    }
    set name(value) {
        if (this._name !== value) {
            this._name = value;
            this.emit('change:name', value);
            //this way is better
            this.updatedAt = new Date();
            //than this way
            this.setUpdatedAt(new Date());
        }
    }

    ////
    private _updatedAt:Date;
    get updatedAt() {
        return this._updatedAt;
    }
    private set updatedAt(value) { //Getter and setter do not agree in visibility
                //some logic and a simple readonly (our absence of a setter) is not enough
        if (this._updatedAt !== value) {
            this._updatedAt = value;
            this.emit('change:updatedAt', value);
        }
    }

    //// method implementation - but what's the point in it?
    private setUpdatedAt(value) {
        if (this._updatedAt !== value) {
            this._updatedAt = value;
            this.emit('change:updatedAt', value);
        }
    }
}

const entity = new Person('Mike');
entity.on('change:updatedAt', console.log.bind(console));
entity.name = 'Thomas';
//but manually setting updatedAt should be forbidden
entity.updatedAt = new Date(); //restricted

Здесь свойство updatedAt на самом деле может иметь сеттер, но оно не должно быть доступно за пределами Person . Кроме того, этот сеттер содержит сложную логику, и простого readonly или отсутствия сеттера недостаточно.

Я согласен с тем, что частный сеттер - это просто синтаксический сахар для частных методов с дополнительной логикой (например, испускание), но я думаю, что это непоследовательно, когда часть логики, связанная с полем, находится в свойстве (геттер), а другая - в методе (частный сеттер) .

Таким образом, геттеры/сеттеры реализованы в C# , я думаю, было бы полезно разрешить различную видимость во время компиляции, хотя эти контракты будут потеряны во время выполнения.

Здесь рекомендуется использовать общедоступный геттер только для чтения и приватное/защищенное резервное поле.

Аксессоры симметричны свойствам в системе типов. все, что мы делаем, должно быть выражено в типе и выражено в свойствах. Добавление новых модификаторов доступа для включения private_set/public_get увеличило бы сложность языка и кривую обучения, а полученная от этого ценность не соответствовала бы добавленной сложности.

В любом случае добавлю мой +1 за это, на случай, если это будет рассмотрено снова в будущем...

Я все еще хотел бы увидеть это. В C# он есть, и он невероятно полезен во многих случаях, особенно когда у вас есть флаги, которые нужно считывать извне для класса, но устанавливаются только внутри с помощью private/protected.

Я думаю, что предположение о том, что он не используется так часто, является фарсом в том смысле, что шаблон не используется, потому что он недоступен для использования.

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

Я согласен с вышеперечисленными пользователями, это стандартно для большинства других языков. Аргумент против этого не имеет веса, и реализацию следует пересмотреть.

Я вообще был удивлен, что вы не можете сделать это в TS... +1 к проблеме.
Не должно быть никакого увеличения кривой обучения с этим

private get x() { ... }
public set x(value) { ... }

Имо, если вы умеете читать по-английски, понятно, что здесь означает частное (защищенное) / общедоступное. Кроме того, если вы определяете средства доступа в первую очередь - вы, вероятно, уже знаете, для чего они нужны.

Ps Насчет ошибки: "Аксессуары геттера и сеттера не согласуются в видимости" - ну вот именно этого я и хочу от них

Вот два варианта использования, где это было бы удобно:

Backbone.js, чтобы избежать уродливых вызовов .get() и .set() :

class Whatever {
    public get rotation(): number {
        return this.get('rotation');
    }
    private set rotation(rotation: number) {
        this.set('rotation', rotation);
    }
}

Свойства, которые могут изменять подклассы:

class ExtendMe {
    public get someProperty(): string {
        // some stuff
    }
    protected set someProperty(prop: string) {
        // some stuff
    }
}

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

Я согласен с людьми, которые просят эту функцию. Я только что попробовал использовать общедоступный метод get() и был удивлен, увидев, что мой set и get должны согласовываться по видимости.

Я читал, что вы, ребята, утверждаете, что это слишком сложно. Вспоминая «способ С++», почему он отличается от:

private _myAttribute: string;
get myAttribute(): string {...}
setMyAttribute(value: string) {...}

Я вижу много вариантов использования для этого, поэтому я здесь прямо сейчас.
Что, если я хочу, чтобы myAttribute был общедоступным, но позволял изменять его только внутри своего класса?
Что, если я хочу добавить некоторую пользовательскую логику каждый раз при изменении атрибута?
Это может быть бизнес-правило, это может быть просто запись в журнал, чтобы понять, почему ему было присвоено определенное значение, вместе с условием точки останова для целей отладки и т. д.

По сути, мы хотим, чтобы метод использовался каждый раз, когда мы изменяем атрибут вместе с синтаксическим сахаром, который создает впечатление, что мы просто присваиваем значение, а не вызываем метод.
В C# есть концепция свойств для этого, и я думал, что TS унаследовал эту концепцию, но запрет на использование различных специальных возможностей является большим ограничением для людей, которые думают так же, как я, и заставляет нас вернуться к «стилю C++».

«Это слишком сложно» никогда не должно быть причиной, чтобы не реализовать невероятно полезную функцию.

Добавляю свой +1. Мне кажется очень странным, что в языке, столь совершенном во многих отношениях, вы не можете этого сделать.

Удивлен, обнаружив, что этот вопрос помечен как закрытый. Эта функция ЖЕЛАЕТ сообщество. Пожалуйста, снова откройте.
+1

12 не решает эту проблему, поэтому эту проблему не следует закрывать комментарием, относящимся к этой проблеме.

ИМХО, это хорошая функция, но, насколько я понимаю, это просто ситуация «ну, я НЕ хочу писать каждый раз _property, вместо этого я хочу использовать частное заданное свойство»

Если есть выбор, либо добавляя огромную сложность к самому коду ядра TypeScript (помните, больше ненужной сложности - меньше скорость разработки, больше времени на отладку, написание тестов, в конце концов - менее частые релизы), но уменьшая написание 1 символа ИЛИ написание этот 1 символ и справляться с ним хорошо... Я бы предпочел писать 1 символ еще раз каждый раз. В конце концов, я не вкладываюсь в ядро ​​TS, и я определенно не хочу разбираться с возможной сложностью этого сахара.

Ребята, это не нарушение условий сделки. Кроме того, требуется мужество и мудрость, чтобы признать запрос слишком сложным для реализации (с результатом, увеличивающим общую сложность кода), так что снимаю шляпу перед вами, ребята, разрабатывающие TypeScript, и спасибо за вашу работу!

(пс зашел узнать почему нет - в итоге передумал и буду разбираться с этим._property=...
и все равно будет доволен)

@idchlife Цель свойства set состоит не в том, чтобы сэкономить разработчикам одно подчеркивание при присвоении значения. Вы также можете добавить некоторую логику в процесс set .

Пожалуйста, откройте этот вопрос снова. Это явно очень желанная функция.

+1 Я бы хотел, чтобы это было пересмотрено.

Очень странно, что ваше обоснование заключается в том, что «это сбивает с толку», когда многие другие языки уже делают это.

Поскольку все 👍 мало что делают, я бы просто спамил еще одним 👍 комментарием.

Это был бы синтаксический сахар, который помогает сделать вещи более читабельными и быстрыми в реализации. Во многих языках есть синтаксический сахар, включая Typescript. Кажется, что нет никаких действительно веских причин, чтобы не делать этого, и слишком сложное никогда не является веской причиной, чтобы что-то не делать, это скорее ленивая отговорка. Решение слишком сложных проблем — одна из причин, по которым большинство людей привлекает разработка программного обеспечения. Так что возьмите эту сложную проблему и решите ее красивым, не таким сложным способом. Потребует ли это рефакторинга и рискованных изменений? Может быть, но именно так все и делается.

Еще желательно. Другие языки без проблем поддерживают различную видимость функций get и set. В довершение ко всему, Typescript создан Microsoft, как и C#, но последний полностью его поддерживает:

public string myPropertyWithLimitedAccess { get; private set; }   

Посмотри на это. Красиво читается, и совершенно ничего сложного.

https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/auto-implemented-properties

Мало причин не поддерживать:

private _myProperty: string;

public get myProperty(): string { return this._myProperty; }
private set myProperty(value: string) { this._myProperty = value; }

Бонусные баллы за странные дизайнерские решения, потому что Typescript был в первую очередь создан для разработчиков .NET, чтобы они привыкли работать с интерфейсом браузера.

+1
Typescript определенно выиграет от этой функциональности!

TypeScript великолепен даже после C#, но это бессмысленное ограничение довольно часто раздражает меня. Пожалуйста, откройте его снова.

увеличит сложность языка

Действительно ли это так сложно по сравнению с такими вещами, как readonly [P in keyof T]: T[P] ?

Удар. Все хотят эту функцию. Разве сообщество TypesScript не должно решать?

увеличит сложность языка

Действительно ли это так сложно по сравнению с такими вещами, как readonly [P in keyof T]: T[P] ?

Сложность вступает в игру с взаимодействием других особенностей языка. К сожалению, я не могу найти его, но IIRC RyanCavanaugh привел пример, в котором эта функция может позволить вам установить, а затем нарушить инварианты через наследование с использованием выражения класса. Просто потому, что написать определенную декларацию легко, это не значит, что всегда будет легко рассуждать о том, как она влияет на вещи.

Вопрос в том, какие проблемы решит данная функция, а какие создаст. На первое ответить легко, на второе ответить на удивление сложно. К сожалению, команда TS иногда, кажется, отвечает «О боже, сложность» вместо того, чтобы проиллюстрировать проблему. (Честно говоря, чем больше времени они тратят на ответ на запрос x в n-й раз, тем меньше времени у них остается на разработку.)

Я не на 100% согласен с идеей «не должно ли решать сообщество», потому что если есть консенсус среди экспертов, фактически разрабатывающих язык, это должно вам кое-что сказать. Но я думаю, что с таким запросом, как это, вдумчивое объяснение от команды о том, в чем заключается компромисс и почему они против этого, не так уж много, чтобы просить.

И лично я думаю, что компромисс в данном случае оправдан на 1000%. Но если я не могу удосужиться его уточнить и заставить работать, полагаю, у меня нет особых прав на жалобы.

Не пора ли вернуться к этому вопросу?

Модификатор readonly великолепен, но есть много случаев, когда вы хотите иметь возможность изменять значение внутри класса и по-прежнему иметь доступ только для чтения снаружи.

Я часто предпочитаю не делать свойство readonly , потому что мне не нравится шум, который добавляет приватное резервное поле + свойства.

Было бы здорово, если бы для вас был синтаксический сахар. Что-то типа:

// Option 1: C# style
public name: string { get; private set; }

// Option 2: Swift style
private(set) name: string

// Option 3: Swift struct-style
public readonly name: string

mutating changeName(name: string) {
  this.name = name
}

// Option 4: New keyword
public frozen name1: string
public readonly name2: string

Мне нравится вариант 2, имхо, он хорошо вписался бы в язык TypeScript.

С вариантом 3 вы можете изменять поля только для чтения только в функциях, помеченных как mutating

С опцией 4 frozen можно установить только в конструкторе, readonly можно установить внутри этого класса, а не какими-либо внешними классами или классами, унаследованными от этого класса.

Для справки, идеи @yvbeek о более гибких модификаторах лучше подходят для обсуждения в https://github.com/microsoft/TypeScript/issues/37487.

Эта проблема предназначена специально для геттеров и сеттеров и имеет очень большое количество голосов! Я думаю, было бы полезно дать геттерам и сеттерам разные модификаторы доступа (и мы можем обновить существующий набор модификаторов в # 37487, если команда TypeScript когда-либо решит, что это приемлемо для продвижения вперед)

Я не на 100% согласен с идеей «не должно ли решать сообщество», потому что если есть консенсус среди экспертов, фактически разрабатывающих язык, это должно вам кое-что сказать. Но я думаю, что с таким запросом, как это, вдумчивое объяснение от команды о том, в чем заключается компромисс и почему они против этого, не так уж много, чтобы просить.

@snarfblam Не хочу засорять эту ветку не относящимся к делу комментарием, но я думаю, что вы раскрыли основной принцип того, как должно работать правительство.

Отсутствие этой функции для меня настоящая боль и (среди прочего) заставило меня переключиться с TS/NodeJS на что-то более... безопасное для типов. Все бы ничего, но когда работаешь над сложным проектом с большим количеством структур данных (много раз глубоко вложенных) и не можешь правильно смоделировать данные, возникает ощущение, что это не язык "для больших мальчики».

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

Эта функция может следовать тому же пути, что и _необязательная цепочка_. Люди будут просить об этой функции в течение многих лет, а затем, наконец, она будет добавлена ​​в язык, потому что это практично, и другие языки предлагают ту же функцию.

В противном случае я надеюсь, что какая-то реализация станет частью EcmaScript, а затем попадет в TypeScript.

Недавно я просто перешел на машинопись, и мне очень понравился этот язык. Я очень расстроен тем, что эта практичная функция, существующая в других языках, не была реализована здесь. Пожалуйста, пересмотрите возможность добавления его в будущем выпуске, независимо от того, насколько сложным, по вашему мнению, может стать язык.

Я добился чего-то похожего на С# с геттерами таким образом:

export class I18nService {
  private static ref: I18nService;

  public static get instance(): I18nService {
    if (!I18nService.ref) {
      I18nService.ref = new I18nService();
    }

    return I18nService.ref;
  }
}

Ошибки типов, подобные приведенным ниже, легко понять и не представляют сложности:

Property 'foo' is writable only in protected scope within class 'Blah' and its subclasses.

или

Property 'foo' is readable only in protected scope within class 'Blah' and its subclasses.

и т. д., и аналогично private .

Вот честно не сложно.

Кстати, я тоже наткнулся на эту проблему, и тем временем я использую такой «хак»:

// somewhere.ts
declare global {
    type Writable<T> = { -readonly [P in keyof T]: T[P]; }
}

// example.ts
class Example {

    public readonly prop: number;

    public doSomething(n: number): void {
        (this as Writable<this>).prop = n;
    }
}

Технически это можно использовать везде, но использование этого обходного пути должно быть ограничено кодом внутри методов класса.

Кстати, я тоже наткнулся на эту проблему, и тем временем я использую такой «хак»:

Я сам обдумывал эту идею, но проблема в том, что у вас нет способа отличить свойства «общедоступные только для чтения» от действительно доступных только для чтения свойств. Это делает его не очень подходящим в качестве универсального решения.

Была ли эта страница полезной?
0 / 5 - 0 рейтинги