Static, private constructors y factories en TypeScript | Nicolás Garzón
TypeScript
Copiar class Product {
static readonly entityName = "product" ;
} Se accede mediante la clase:
TypeScript
Copiar Product. entityName; No mediante una instancia.
TypeScript
Copiar class Product {
static create ( input: CreateProductInput) : Product {
return new Product (
createProductId ( ) ,
input. name,
input. price,
) ;
}
} TypeScript
Copiar class Registry {
static readonly entries = new Map< string , Handler> ( ) ;
static {
Registry. entries. set ( "default" , defaultHandler) ;
}
} Se ejecuta durante evaluación de la clase. Introduce efectos de import si la clase está en un módulo.
TypeScript
Copiar class Email {
private constructor ( readonly value: string ) { }
static parse ( value: unknown ) : Email {
if ( typeof value !== "string" || ! value. includes ( "@" ) ) {
throw new TypeError ( "Invalid email" ) ;
}
return new Email ( value) ;
}
} Obliga a consumidores TypeScript a usar la factory.
private constructor no está obsoleto ni “prohibido”; es apropiado cuando la construcción debe pasar por una API controlada.
El modificador private se elimina. Si necesitas impedir construcción desde JavaScript externo en runtime, una clase exportada no ofrece una barrera absoluta solo por el modificador TypeScript. Ocultar el constructor mediante un módulo o retornar un objeto puede ser más fuerte.
TypeScript
Copiar abstract class Entity {
protected constructor ( readonly id: string ) { }
} Permite subclases, pero no instanciación externa directa.
TypeScript
Copiar class ApplicationConfig {
static #instance: ApplicationConfig | undefined ;
private constructor ( readonly value: ConfigValue) { }
static getInstance ( ) : ApplicationConfig {
return this . #instance ??=
new ApplicationConfig ( loadConfig ( ) ) ;
}
} Un singleton crea estado global implícito, complica tests y ciclos de vida. El constructor private lo permite, pero no vuelve el patrón automáticamente recomendable.
Inyección explícita suele ser mejor:
TypeScript
Copiar const config = new ApplicationConfig ( value) ;
createApplication ( { config } ) ; TypeScript
Copiar function createProductService (
dependencies: ProductServiceDependencies,
) : ProductService {
return new ProductService ( dependencies) ;
} Puede ocultar la clase y devolver una interface.
TypeScript
Copiar class Box< T > {
private constructor ( readonly value: T ) { }
static of < TValue> ( value: TValue) : Box< TValue> {
return new Box ( value) ;
}
} El método static usa su propio generic.
Constructores no pueden ser async.
TypeScript
Copiar class DatabaseClient {
private constructor ( private readonly connection: Connection) { }
static async connect (
configuration: DatabaseConfig,
) : Promise < DatabaseClient> {
const connection = await openConnection ( configuration) ;
return new DatabaseClient ( connection) ;
}
} TypeScript
Copiar type ProductConstructor = {
new (
id: ProductId,
name: string ,
) : Product;
readonly entityName: string ;
} ; Describe lado static y new.
TypeScript
Copiar class Model {
static create < T extends typeof Model> (
this : T ,
) : InstanceType< T > {
return new this ( ) as InstanceType< T > ;
}
} Este patrón avanzado puede preservar subclases, pero constructors protegidos, argumentos y assertions complican el diseño. No lo uses cuando una factory explícita es más clara.
Las clases heredan propiedades estáticas de Function como name, length y prototype. TypeScript limita o rechaza ciertos miembros static con nombres conflictivos.
TypeScript
Copiar class Percentage {
private constructor ( readonly value: number ) { }
static create ( value: number ) : Percentage {
if ( ! Number. isFinite ( value) || value < 0 || value > 100 ) {
throw new RangeError ( "Percentage must be between 0 and 100" ) ;
}
return new Percentage ( value) ;
}
}
Decir que private constructors ya no deben usarse.
Crear singleton por defecto.
Intentar hacer async un constructor.
Usar el generic de instancia dentro de static.
Creer que static members viven en cada instancia.
Ejecutar efectos importantes en static blocks sin control.
Exponer clase y factory cuando solo una forma debería ser pública.
Confundir restricción TypeScript con seguridad runtime absoluta.
Static pertenece al constructor.
Private constructor controla la API estática de creación.
Protected constructor reserva creación para subclases.
Factories pueden validar, elegir implementación y ser async.
Un método static genérico tiene sus propios type parameters.
Singleton es una decisión de estado global, no una consecuencia necesaria.
La barrera private desaparece en el JavaScript emitido.
¿Por qué una factory static es apropiada para abrir una conexión antes de crear una instancia?
Respuesta Porque los constructores no pueden ser async, mientras una factory puede esperar la conexión y retornar Promise<Instance>.
Mixins y clases genéricas combina capacidades mediante factories de clases y muestra sus límites.