Modificadores, parameter properties y readonly | Nicolás Garzón
TypeScript
Copiar class Product {
public name: string ;
} Es el valor por defecto. El miembro puede utilizarse desde cualquier código que tenga una instancia compatible.
TypeScript
Copiar class Account {
private balance = 0 ;
} El checker restringe acceso fuera de la clase.
TypeScript
Copiar account. balance; Produce error estático, pero private no es necesariamente una barrera runtime. Si necesitas privacidad durante ejecución, utiliza #balance.
TypeScript
Copiar class Entity {
protected updatedAt = new Date ( ) ;
}
class Product extends Entity {
touch ( ) {
this . updatedAt = new Date ( ) ;
}
} Es accesible dentro de la clase y subclases, no desde consumidores externos.
protected aumenta acoplamiento entre jerarquías: una subclase conoce detalles internos de la base.
TypeScript
Copiar class Account {
#balance = 0 ;
} Pertenece a JavaScript y se comprueba en runtime. No puede accederse mediante una subclase ni usando una string dinámica ordinaria.
TypeScript
Copiar class Session {
constructor ( private token: string ) { }
} Crea una parameter property privada y asigna el argumento.
TypeScript
Copiar class Product {
readonly id: string ;
name: string ;
constructor ( id: string , name: string ) {
this . id = id;
this . name = name;
}
} TypeScript
Copiar class Product {
constructor (
readonly id: string ,
public name: string ,
) { }
} Solo un parámetro con modificador de acceso, readonly o ambos se convierte en propiedad.
Parameter properties aportan cuando la clase solo almacena dependencias o valores.
TypeScript
Copiar class ProductService {
constructor (
private readonly repository: ProductRepository,
private readonly logger: Logger,
) { }
} En constructores con lógica compleja, propiedades explícitas pueden hacer más visible la inicialización.
TypeScript
Copiar class Product {
constructor ( readonly id: string ) { }
} Puede asignarse durante declaración o constructor, pero no después mediante el tipo.
TypeScript
Copiar product. id = "p2" ; TypeScript
Copiar class Order {
constructor (
readonly customer: {
name: string ;
} ,
) { }
}
order. customer. name = "Ana" ; La referencia customer no puede reemplazarse, pero su objeto sigue mutable.
TypeScript
Copiar class Product {
#price = 0 ;
get price ( ) : number {
return this . #price;
}
} Un getter sin setter presenta una propiedad de solo lectura para consumidores, aunque la clase pueda cambiar su estado interno.
TypeScript
Copiar class Cart {
#items: CartItem[ ] = [ ] ;
get items ( ) : readonly CartItem[ ] {
return this . #items;
}
} Impide que el consumidor use push mediante esa referencia, pero los elementos pueden seguir mutables.
TypeScript
Copiar class Controller {
private client! : ApiClient;
initialize ( client: ApiClient) {
this . client = client;
}
} ! silencia strictPropertyInitialization. No añade una comprobación ni un valor.
Úsalo únicamente cuando un ciclo externo garantiza inicialización antes de cualquier lectura.
TypeScript
Copiar class FrameworkController {
declare context: FrameworkContext;
} Comunica que la propiedad existe por otro mecanismo y evita emitir el field. Es útil con herencia o decorators, pero puede mentir igual que una assertion.
TypeScript
Copiar class Product {
accessor name: string ;
} Los auto-accessors de JavaScript/TypeScript crean almacenamiento oculto con getter y setter, y son relevantes para decorators modernos. No equivalen exactamente a un field público simple.
TypeScript
Copiar class BaseService {
execute ( ) : void { }
}
class ProductService extends BaseService {
override execute ( ) : void { }
} override confirma que existe un miembro compatible en la base. Con noImplicitOverride, TypeScript lo exige.
Fields de base y subclase siguen reglas runtime de JavaScript. Un field de subclase puede sobrescribir un valor establecido por el constructor base después de super.
No llames métodos sobreescribibles desde constructores base si dependen de fields de la subclase todavía no inicializados.
TypeScript
Copiar class ProductService {
constructor (
private readonly repository: ProductRepository,
private readonly clock: Clock,
) { }
async create ( input: CreateProductInput) : Promise < Product> {
const product = createProduct ( input, this . clock. now ( ) ) ;
await this . repository. save ( product) ;
return product;
}
}
Confundir private con #private.
Usar protected para compartir demasiados detalles.
Creer que readonly congela objetos.
Abusar de parameter properties en constructores difíciles de leer.
Utilizar ! sin una garantía de inicialización.
Usar declare como si creara la propiedad.
Sobrescribir un método sin override en proyectos estrictos.
Depender de fields de subclase durante el constructor base.
public es el acceso predeterminado.
private y protected son controles estáticos de TypeScript.
#private existe durante ejecución.
Parameter properties crean y asignan miembros.
readonly impide reasignación, no mutación profunda.
! y declare son promesas al checker.
override protege cambios en jerarquías.
¿Por qué private balance y #balance no ofrecen la misma garantía?
Respuesta Porque private restringe acceso durante el análisis, mientras #balance usa la privacidad nativa de JavaScript y también se aplica durante ejecución.
implements, abstract classes y contratos distingue comprobación estructural de reutilización de implementación.