Typescript: Modificador de acesso diferente para getter e setter

Criado em 21 abr. 2015  ·  40Comentários  ·  Fonte: microsoft/TypeScript

Seria possível implementar modificadores de acesso diferentes para getter e setter? Dessa forma, o setter pode ser, por exemplo, privado/protegido e o getter, público. Em alguns casos isso é realmente útil quando o valor deve ser somente leitura.

Exemplo:

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

Comentários muito úteis

De qualquer forma, adicionando meu +1 para isso, caso seja considerado novamente no futuro ...

Todos 40 comentários

12

Como o nº 12 está fechado com o nº 6532, agora parece que esse problema está fora do escopo.
Agora você tem planos de implementar diferentes modificadores de acesso?
Porque readonly não é suficiente para resolver o que é descrito aqui como uma propriedade que pode ser escrita dentro de uma classe.

Acredito que também devo mover meu exemplo aqui do problema relacionado:

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

Aqui, a propriedade updatedAt pode ter um setter, mas não deve ser acessível fora de Person . Além disso, este setter contém lógica complexa e um simples readonly ou a ausência de um setter não é suficiente.

Concordo que o setter privado é apenas um açúcar de sintaxe para métodos privados com lógica adicional (como emissão), mas acho inconsistente quando parte da lógica relacionada a um campo está em uma propriedade (getter) e outra em um método (setter privado) .

Desta forma getters/setters são implementados em C# , acho que seria útil permitir visibilidade diferente em tempo de compilação, embora esses contratos sejam perdidos em tempo de execução.

A recomendação aqui é usar um getter público somente leitura e um campo de apoio privado/protegido.

Os acessadores são simétricos com propriedades no sistema de tipos. tudo o que fizermos precisará ser manifestado no tipo e exprimível nas propriedades. Adicionar novos modificadores de acesso para habilitar private_set/public_get aumentaria a complexidade da linguagem e a curva de aprendizado, e o valor obtido com isso não corresponderia à complexidade adicionada.

De qualquer forma, adicionando meu +1 para isso, caso seja considerado novamente no futuro ...

Eu ainda gostaria de ver isso. O C# tem isso e é incrivelmente útil em muitas circunstâncias, especialmente quando você tem sinalizadores que desejam ser lidos externamente para a classe, mas definidos apenas internamente com private/protected.

Eu acho que a sugestão de que ele não é usado com tanta frequência é uma farsa, pois o padrão não é usado porque não está disponível para uso.

Esta é uma boa adição à linguagem: A quantidade de complexidade que isso adicionaria (da perspectiva de um programador de typescript) é pequena. O conceito é fácil de entender e o compilador deve ser capaz de fornecer boas mensagens de erro sugerindo por que você não pode acessar getters ou setters devido ao escopo.

Concordo com os usuários acima, isso é padrão para a maioria dos outros idiomas. O argumento contra ela não tem peso e a implementação deve ser reconsiderada.

Eu realmente fiquei surpreso que você não pode fazer isso no TS... +1 para o problema.
Não deve haver nenhuma curva de aprendizado aumentada com isso

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

Imo se você pode ler inglês é auto-explicativo o que significa privado (protegido)/público aqui. Além disso, se você estiver definindo os acessadores em primeiro lugar - provavelmente já sabe para que servem.

Ps Sobre o erro: "Os acessadores getter e setter não concordam em visibilidade" - bem: é exatamente isso que eu quero que eles façam

Aqui estão dois casos de uso em que isso seria útil:

Backbone.js, para evitar chamadas feias de .get() e .set() :

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

Propriedades que as subclasses podem modificar:

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

Eu definitivamente gostaria de ver isso, pois tem me incomodado desde que comecei a usar o TypeScript.

Concordo com as pessoas que solicitam esse recurso. Eu apenas tentei ter um método get() público e fiquei surpreso ao ver que meu set e get devem concordar com a visibilidade.

Eu tenho lido vocês dizendo que é muito complexo. Lembrando do "jeito C++" porque é diferente de:

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

Eu posso ver muitos casos de uso para isso, é por isso que estou aqui agora.
E se eu quiser que myAttribute seja acessível publicamente, mas permita apenas ser modificado dentro de sua classe?
E se eu quiser adicionar alguma lógica personalizada toda vez que o atributo for modificado?
Pode ser uma regra de negócio, pode ser apenas um registro para entender por que foi atribuído um valor específico, juntamente com uma condição de ponto de interrupção para fins de depuração, etc...

Basicamente, queremos que um método seja usado toda vez que modificarmos o atributo junto com o açúcar sintático que faz parecer que estamos apenas atribuindo um valor, em vez de chamar um método.
C# tem o conceito de propriedades para isso e eu pensei que TS herdou esse conceito, mas não permitir diferentes acessibilidades é uma grande limitação para pessoas que pensam como eu e nos fazem voltar ao "estilo C++".

"É muito difícil" nunca deve ser motivo para não implementar um recurso incrivelmente útil.

Adicionando meu +1. Parece muito estranho para mim que em uma linguagem que é tão completa em tantos aspectos você não possa fazer isso.

Surpreso ao descobrir que este problema está marcado como encerrado. Este recurso é DESEJADO pela comunidade. Por favor, reabra.
+1

12 não aborda esse problema, portanto, este problema não deve ser encerrado com um comentário que se refira a esse problema.

IMHO, este é um bom recurso, mas tão justo quanto eu entendo, é apenas a situação "bem, eu NÃO quero escrever toda vez que _property, em vez disso, quero usar a propriedade do conjunto privado"

Se houver escolha, adicionar uma enorme complexidade ao próprio código principal do TypeScript (lembre-se, mais complexidade desnecessária - menos velocidade de desenvolvimento de coisas, mais tempo para depurar, escrever testes, no final - lançamentos menos frequentes), mas reduzindo a gravação de 1 símbolo OU a gravação aquele 1 símbolo e lidar bem com ele... Prefiro escrever 1 símbolo mais uma vez todas as vezes. No final das contas, não sou contribuinte do núcleo TS e definitivamente não quero lidar com a possível complexidade desse açúcar.

Não é um dealbreaker caras. Também é preciso coragem e sabedoria para identificar a solicitação como muito complexa para implementar (com o resultado aumentando a complexidade geral do código), então tire o chapéu para vocês que estão desenvolvendo o TypeScript e obrigado pelo seu trabalho!

(ps vim aqui saber porque não - no final mudei de ideia e vou lidar com isso._property = ...
e ainda será feliz)

@idchlife O objetivo da propriedade set não é salvar os desenvolvedores um sublinhado ao atribuir valor. Você também pode querer adicionar alguma lógica no processo set .

Por favor, reabra este problema. Este é claramente um recurso altamente desejado.

+1 Eu adoraria ver isso reconsiderado.

Muito estranho seu raciocínio é "isso tornaria as coisas confusas" quando muitas outras linguagens já fazem isso.

Como todos os 👍s não estão fazendo muito, eu apenas enviaria spam com mais um 👍 comentário.

Isso seria açúcar sintático que ajuda a tornar as coisas mais legíveis e rápidas de implementar. Muitas linguagens têm açúcar sintático, incluindo Typescript. Não parece haver nenhum bom raciocínio para não fazer isso e muito complexo nunca é uma boa razão para não fazer algo, é mais uma desculpa preguiçosa. Resolver problemas que são muito complexos é uma das razões pelas quais a maioria das pessoas é atraída pela engenharia de software. Então pegue esse problema complexo e resolva-o de uma maneira agradável e não tão complexa. Exigirá refatoração e mudanças arriscadas? Talvez, mas é assim que as coisas são feitas.

Ainda desejável. Outras linguagens não têm problemas para suportar visibilidades diferentes para a funcionalidade get e set. Para adicionar dano ao insulto, o Typescript é feito pela Microsoft, assim como o C#, mas o último o suporta totalmente:

public string myPropertyWithLimitedAccess { get; private set; }   

Olhe para isso. Lindamente legível e totalmente nada complexo.

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

Há poucas razões para não apoiar:

private _myProperty: string;

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

Pontos de bônus em decisões de design estranhas, porque o Typescript foi feito principalmente para desenvolvedores .NET se acostumarem a trabalhar com o front-end do navegador.

+1
O Typescript definitivamente se beneficiaria dessa funcionalidade!

O TypeScript é ótimo mesmo depois do C#, mas essa limitação inútil me irrita com bastante frequência. Por favor, reabra-o.

aumentaria a complexidade da linguagem

Isso é realmente tão complexo em comparação com coisas como readonly [P in keyof T]: T[P] ?

Ressalto. Todo mundo quer esse recurso. A comunidade TypesScript não deveria decidir?

aumentaria a complexidade da linguagem

Isso é realmente tão complexo em comparação com coisas como readonly [P in keyof T]: T[P] ?

A complexidade entra em jogo com a interação de outros recursos da linguagem. Infelizmente, não consigo encontrá-lo, mas o IIRC RyanCavanaugh deu um exemplo em que esse recurso pode permitir que você configure e viole invariantes por meio de herança usando uma expressão de classe. Só porque é fácil escrever uma determinada declaração, isso não significa que sempre será fácil raciocinar sobre como isso afeta as coisas.

A questão é quais problemas um determinado recurso resolverá e quais ele criará. O primeiro é fácil de responder, o último pode ser surpreendentemente difícil de responder. Infelizmente, a equipe TS às vezes parece responder com "complexidade OMG" em vez de ilustrar o problema. (Para ser justo, quanto mais tempo eles gastam respondendo à pergunta x pela enésima vez, menos tempo eles têm para desenvolver.)

Eu não concordo 100% com a noção de "a comunidade não deveria decidir", porque se houver consenso entre os especialistas que estão desenvolvendo a linguagem, isso deve lhe dizer algo. Mas acho que com algo tão solicitado quanto isso, uma explicação cuidadosa da equipe sobre qual é a troca e por que eles são contra não é pedir muito.

E, pessoalmente, acho que a troca vale 1000% a pena neste caso. Mas se não posso me dar ao trabalho de especificar e fazer funcionar, suponho que não tenho muito direito de reclamar.

Seria este um bom momento para revisitar esta questão?

O modificador readonly é ótimo, mas há muitos casos em que você deseja alterar o valor dentro da classe e ainda ter tudo como somente leitura do lado de fora.

Muitas vezes escolho não tornar a propriedade readonly porque não gosto do ruído que um campo de apoio privado + as propriedades adicionam.

Seria ótimo se houvesse algum açúcar sintático para fazer isso por você. Algo como:

// 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

Eu gosto da opção 2, imo se encaixaria bem na linguagem TypeScript.

Com a opção 3, você só pode alterar campos somente leitura em funções marcadas como mutating

Com a opção 4, frozen só pode ser definido no construtor, readonly pode ser definido dentro desta classe, não por quaisquer classes externas ou classes herdadas desta classe.

Para referência, as ideias de @yvbeek sobre modificadores mais flexíveis são mais adequadas para a discussão em https://github.com/microsoft/TypeScript/issues/37487.

Esta edição é específica para getters e setters, e tem um número super alto de upvotes! Acho que seria útil fornecer modificadores de acesso diferentes para getters e setters (e podemos atualizar o conjunto existente de modificadores em # 37487 se a equipe do TypeScript decidir que é uma coisa aceitável para seguir em frente)

Eu não concordo 100% com a noção de "a comunidade não deveria decidir", porque se houver consenso entre os especialistas que estão desenvolvendo a linguagem, isso deve lhe dizer algo. Mas acho que com algo tão solicitado quanto isso, uma explicação cuidadosa da equipe sobre qual é a troca e por que eles são contra não é pedir muito.

@snarfblam Para não entupir este tópico com um comentário irrelevante, mas acho que você revelou um princípio fundamental para a maneira como o governo deve operar.

Não ter esse recurso é uma verdadeira dor para mim e (entre outras coisas) me fez mudar de TS/NodeJS para algo mais... tipo seguro. Está tudo bem e elegante, mas quando você está trabalhando em um projeto complexo com muitas estruturas de dados (aninhadas muitas vezes) e você não pode modelar os dados corretamente, você tem a sensação de que esta não é uma linguagem "para grandes Rapazes".

No meu caso particular, quero que a propriedade seja somente leitura, mas modificável por dentro... e também serializada para JSON. Muitos aros para pular.

Esse recurso pode seguir o mesmo caminho que o _encadeamento opcional_. As pessoas vão pedir esse recurso por anos e, finalmente, ele será adicionado ao idioma, porque é prático e outros idiomas oferecem o mesmo recurso.

Caso contrário, espero que alguma implementação se torne parte do EcmaScript e depois vá para o TypeScript.

Recentemente, mudei para o texto datilografado e estou adorando a linguagem. Estou realmente chateado que esse recurso prático que existe em outras linguagens não tenha sido implementado aqui. Por favor, reconsidere adicioná-lo em uma versão futura, independentemente de quanta complexidade você acha que pode adicionar à linguagem.

Consegui algo semelhante ao c# com getters dessa maneira:

export class I18nService {
  private static ref: I18nService;

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

    return I18nService.ref;
  }
}

Erros de digitação como os seguintes são fáceis de entender e não são complicados:

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

ou

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

etc, e similar com private .

Isso honestamente não é complicado.

BTW, também me deparei com esse problema e estou usando esse tipo de "hack" nesse meio tempo:

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

Tecnicamente, isso pode ser usado em todos os lugares, mas o uso dessa solução alternativa deve ser restrito ao código dentro dos métodos de classe.

BTW, também me deparei com esse problema e estou usando esse tipo de "hack" nesse meio tempo:

Eu mesmo brinquei com essa ideia, mas o problema é que você não tem como distinguir entre propriedades "somente leitura pública" e realmente somente leitura. Isso o torna pouco adequado como uma solução de uso geral.

Esta página foi útil?
0 / 5 - 0 avaliações

Questões relacionadas

manekinekko picture manekinekko  ·  3Comentários

siddjain picture siddjain  ·  3Comentários

seanzer picture seanzer  ·  3Comentários

fwanicka picture fwanicka  ·  3Comentários

dlaberge picture dlaberge  ·  3Comentários