Typescript: Modificador de acceso diferente para getter y setter

Creado en 21 abr. 2015  ·  40Comentarios  ·  Fuente: microsoft/TypeScript

¿Sería posible implementar diferentes modificadores de acceso para getter y setter? De esta forma, el setter podría ser, por ejemplo, privado/protegido y el captador público. En algunos casos, esto es realmente útil cuando el valor será de solo lectura.

Ejemplo:

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

Comentario más útil

De todos modos, agrego mi +1 para esto, en caso de que se considere nuevamente en el futuro ...

Todos 40 comentarios

12

Como el #12 está cerrado con el #6532 ahora parece que este problema está fuera de alcance.
¿Tiene planes para implementar diferentes modificadores de acceso?
Porque readonly no es suficiente para resolver lo que se describe aquí, ya que una propiedad podría escribirse dentro de una clase.

Creo que también debería mover mi ejemplo aquí desde un 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

Aquí, la propiedad updatedAt realidad puede tener un setter pero no debería ser accesible fuera de Person . Además, este setter contiene una lógica compleja y una simple lectura o la ausencia de un setter no es suficiente.

Estoy de acuerdo en que el setter privado es solo un azúcar de sintaxis para métodos privados con lógica adicional (como emitir), pero creo que es inconsistente cuando parte de la lógica relacionada con un campo está en una propiedad (captador) y otra en un método (setter privado) .

De esta forma, los getters/setters se implementan en C# , creo que sería útil permitir una visibilidad diferente en el momento de la compilación, aunque estos contratos se perderán en el tiempo de ejecución.

La recomendación aquí es utilizar un captador público de solo lectura y un campo de respaldo privado/protegido.

Los accesores son simétricos con las propiedades en el sistema de tipos. todo lo que hagamos tendrá que manifestarse en el tipo y expresarse en las propiedades. Agregar nuevos modificadores de acceso para habilitar private_set/public_get aumentaría la complejidad del idioma y la curva de aprendizaje, y el valor obtenido de esto no coincidiría con la complejidad agregada.

De todos modos, agrego mi +1 para esto, en caso de que se considere nuevamente en el futuro ...

Todavía me gustaría ver esto. C# lo tiene, y es increíblemente útil en muchas circunstancias, especialmente cuando tiene indicadores que desean leerse externamente a la clase, pero solo se configuran internamente con privado/protegido.

Creo que la sugerencia de que no se usa con tanta frecuencia es una farsa porque el patrón no se usa porque no está disponible para usar.

Esta es una buena adición al lenguaje: la cantidad de complejidad que esto agregaría (desde la perspectiva de un programador de mecanografiado) es pequeña. El concepto es fácil de entender y el compilador debería poder proporcionar buenos mensajes de error que insinúen por qué no puede acceder a getters o setters debido al alcance.

Estoy de acuerdo con los usuarios anteriores, esto es estándar para la mayoría de los demás idiomas. El argumento en contra no tiene peso y la implementación debe ser reconsiderada.

De hecho, me sorprendió que no puedas hacer esto en TS... +1 al problema.
No debería haber una mayor curva de aprendizaje con esto

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

Imo, si puede leer inglés, se explica por sí mismo lo que significa privado (protegido) / público aquí. Además, si está definiendo los accesores en primer lugar, probablemente ya sepa para qué sirven.

Ps Acerca del error: "Los accesores de getter y setter no están de acuerdo en visibilidad" - bueno: eso es exactamente lo que quiero que hagan

Aquí hay dos casos de uso en los que esto sería útil:

Backbone.js, para evitar las feas llamadas .get() y .set() :

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

Propiedades que las subclases pueden modificar:

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

Definitivamente me gustaría ver esto, ya que me ha estado molestando desde que comencé a usar TypeScript.

Estoy de acuerdo con las personas que solicitan esta función. Intenté tener un método get() público y me sorprendió ver que mi set y get deben estar de acuerdo con la visibilidad.

Los he estado leyendo diciendo que es demasiado complejo. Recordando el "modo C++" por qué es diferente de:

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

Puedo ver muchos casos de uso para esto, es por eso que estoy aquí ahora.
¿Qué pasa si quiero que myAttribute sea de acceso público pero solo permita que se modifique dentro de su clase?
¿Qué pasa si quiero agregar alguna lógica personalizada cada vez que se modifica el atributo?
Puede ser una regla de negocio, puede ser solo un registro para comprender por qué se le ha asignado un valor específico, junto con una condición de punto de interrupción para fines de depuración, etc.

Básicamente, queremos que se use un método cada vez que modificamos el atributo junto con el azúcar sintáctico que hace que parezca que solo estamos asignando un valor, en lugar de llamar a un método.
C # tiene el concepto de propiedades para esto y pensé que TS heredó ese concepto, pero no permitir diferentes accesibilidades es una gran limitación para las personas que piensan como yo y nos hacen volver al "estilo C ++".

"Es demasiado difícil" nunca debería ser una razón para no implementar una característica increíblemente útil.

Agregando mi +1. Me parece muy extraño que en un idioma que es tan completo en tantos aspectos no puedas hacer esto.

Me sorprende descubrir que este problema está marcado como cerrado. Esta característica es DESEADA por la comunidad. Vuelva a abrir.
+1

12 no aborda este problema, por lo que este problema no debe cerrarse con un comentario que se refiera a ese problema.

En mi humilde opinión, esta es una buena característica, pero por lo que entiendo, es solo la situación "bueno, NO quiero escribir cada vez que _property en lugar de eso, quiero usar la propiedad de conjunto privado"

Si hay una opción, ya sea agregar una gran complejidad al código central de TypeScript (recuerde, más complejidad innecesaria: menos velocidad de desarrollo de cosas, más tiempo para depurar, escribir pruebas, al final, lanzamientos menos frecuentes) pero reducir la escritura 1 símbolo O escribir ese 1 símbolo y manejarlo bien... Prefiero escribir 1 símbolo una vez más cada vez. Al final, no contribuyo al núcleo de TS y definitivamente no quiero lidiar con la posible complejidad de este azúcar.

No es un factor decisivo chicos. También se necesita coraje y sabiduría para detectar la solicitud como demasiado compleja para implementar (con un resultado que aumenta la complejidad general del código), así que felicitaciones por el desarrollo de TypeScript y gracias por su trabajo.

(ps vine aquí para saber por qué no - al final cambié de opinión y me ocuparé de esto._property = ...
y seguirá siendo feliz)

@idchlife El objetivo de la propiedad set no es ahorrarles a los desarrolladores un guión bajo al asignar valor. También es posible que desee agregar algo de lógica en el proceso set .

Vuelva a abrir este problema. Esta es claramente una característica muy deseada.

+1 Me encantaría ver esto reconsiderado.

Muy extraño, su razonamiento es "esto haría las cosas confusas" cuando muchos otros idiomas ya lo hacen.

Dado que todos los 👍 no están haciendo mucho, solo enviaría spam con un comentario 👍 más.

Esto sería azúcar sintáctico que ayuda a que las cosas sean más legibles y rápidas de implementar. Muchos idiomas tienen azúcar sintáctico, incluido Typescript. No parece haber ningún buen razonamiento real para no hacerlo y demasiado complejo nunca es una buena razón para no hacer algo, es más una evasión perezosa. Resolver problemas que son demasiado complejos es una de las razones por las que la mayoría de las personas se sienten atraídas por la ingeniería de software. Así que toma este problema complejo y resuélvelo de una manera agradable y no tan compleja. ¿Requerirá refactorización y cambios arriesgados? Tal vez, pero así es como se hacen las cosas.

Todavía deseable. Otros lenguajes no tienen problemas para admitir diferentes visibilidades para la funcionalidad de obtención y configuración. Para colmo de males, Typescript está hecho por Microsoft, al igual que C #, pero este último lo admite por completo:

public string myPropertyWithLimitedAccess { get; private set; }   

Mira eso. Maravillosamente legible, y nada complejo.

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

Hay pocas razones para no apoyar:

private _myProperty: string;

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

Puntos de bonificación en decisiones de diseño extrañas, porque Typescript se hizo principalmente para que los desarrolladores de .NET se acostumbren más a trabajar con el front-end del navegador.

+1
¡Mecanografiado definitivamente se beneficiaría de esta funcionalidad!

TypeScript es excelente incluso después de C #, pero esta limitación sin sentido me irrita con bastante frecuencia. Vuelva a abrirlo.

aumentaría la complejidad del lenguaje

¿Es realmente este complejo en comparación con cosas como readonly [P in keyof T]: T[P] ?

Protuberancia. Todo el mundo quiere esta función. ¿No debería decidir la comunidad de TypesScript?

aumentaría la complejidad del lenguaje

¿Es realmente este complejo en comparación con cosas como readonly [P in keyof T]: T[P] ?

La complejidad entra en juego con la interacción de otras características del lenguaje. Desafortunadamente, no puedo encontrarlo, pero IIRC RyanCavanaugh dio un ejemplo en el que esta característica podría permitirle configurar y luego violar invariantes a través de herencia usando una expresión de clase. El hecho de que sea fácil escribir una determinada declaración no significa que siempre será fácil razonar sobre cómo afecta las cosas.

La pregunta es qué problemas resolverá una determinada característica y cuáles creará. El primero es fácil de responder, el segundo puede ser sorprendentemente difícil de responder. Desafortunadamente, el equipo de TS a veces parece responder con "complejidad Dios mío" en lugar de ilustrar el problema. (Para ser justos, cuanto más tiempo pasan respondiendo la consulta x por enésima vez, menos tiempo tienen para desarrollar).

No estoy 100% de acuerdo con la noción de "no debería decidir la comunidad", porque si hay consenso entre los expertos que están desarrollando el lenguaje, eso debería decirte algo. Pero creo que con algo tan solicitado como esto, una explicación detallada del equipo sobre cuál es la compensación y por qué están en contra no es mucho pedir.

Y personalmente, creo que la compensación vale la pena al 1000% en este caso. Pero si no puedo molestarme en especificarlo y hacerlo funcionar, supongo que no tengo mucho derecho a quejarme.

¿Sería este un buen momento para revisar este tema?

El modificador readonly es excelente, pero hay muchos casos en los que desea poder cambiar el valor dentro de la clase y aún así tener todo fuera de solo lectura.

A menudo elijo no hacer que la propiedad sea readonly porque no me gusta el ruido que un campo de respaldo privado + las propiedades agregan.

Sería genial si hubiera algo de azúcar sintáctico para hacer esto por ti. 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

Me gusta la opción 2, en mi opinión encajaría bien en el lenguaje TypeScript.

Con la opción 3, solo puede cambiar campos de solo lectura en funciones que están marcadas como mutating

Con la opción 4, frozen solo se puede configurar en el constructor, readonly se puede configurar dentro de esta clase, no por clases externas o clases que hereden de esta clase.

Como referencia, las ideas de @yvbeek sobre modificadores más flexibles se adaptan mejor a la discusión en https://github.com/microsoft/TypeScript/issues/37487.

¡Este problema es específicamente para captadores y definidores, y tiene una gran cantidad de votos a favor! Creo que sería útil dar a los getters y setters diferentes modificadores de acceso (y podemos actualizar el conjunto existente de modificadores en #37487 si el equipo de TypeScript alguna vez decide que es aceptable seguir adelante)

No estoy 100% de acuerdo con la noción de "no debería decidir la comunidad", porque si hay consenso entre los expertos que están desarrollando el lenguaje, eso debería decirte algo. Pero creo que con algo tan solicitado como esto, una explicación detallada del equipo sobre cuál es la compensación y por qué están en contra no es mucho pedir.

@snarfblam No quiero obstruir este hilo con un comentario irrelevante, pero creo que ha revelado un principio fundamental sobre la forma en que debe operar el gobierno.

No tener esta característica es un verdadero dolor para mí y (entre otras cosas) me hizo cambiar de TS/NodeJS a algo más... tipo seguro. Todo está muy bien, pero cuando trabajas en un proyecto complejo con muchas estructuras de datos (muchas veces profundamente anidadas) y no puedes modelar los datos correctamente, tienes la sensación de que este no es un lenguaje "para grandes Niños".

En mi caso particular, quiero que la propiedad sea de solo lectura, pero modificable desde adentro... y también serializada a JSON. Demasiados aros por los que saltar.

Esta función podría seguir el mismo camino que el _encadenamiento opcional_. La gente pedirá esta función durante años y finalmente se agregará al idioma, porque es práctico y otros idiomas ofrecen la misma función.

De lo contrario, espero que alguna implementación se convierta en parte de EcmaScript y luego llegue a TypeScript.

Recientemente cambié a mecanografiado y me ha encantado el idioma. Estoy realmente molesto porque esta función práctica que existe en otros idiomas no se ha implementado aquí. Reconsidere agregarlo en una versión futura, independientemente de la complejidad que crea que podría agregar al idioma.

Logré algo similar a C# con captadores de esa manera:

export class I18nService {
  private static ref: I18nService;

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

    return I18nService.ref;
  }
}

Los errores tipográficos como los siguientes son fáciles de entender y no son complicados:

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

o

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

etc, y similares con private .

Honestamente, eso no es complicado.

Por cierto, también me encontré con este problema y, mientras tanto, estoy usando este tipo de "truco":

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

Técnicamente, esto podría usarse en todas partes, pero el uso de esta solución debe restringirse al código dentro de los métodos de clase.

Por cierto, también me encontré con este problema y, mientras tanto, estoy usando este tipo de "truco":

Yo mismo he jugado con esta idea, pero el problema es que no tiene forma de distinguir entre "solo lectura pública" y propiedades verdaderamente de solo lectura. Esto hace que no sea muy adecuado como solución de propósito general.

¿Fue útil esta página
0 / 5 - 0 calificaciones