Un constructor en TypeScript es el método constructor de la clase con parámetros tipados. No lleva anotación de tipo de retorno: siempre produce una instancia de la clase.
Escribir constructor(): Product es el error TS1093 (Type annotation cannot appear on a constructor declaration). Todo lo demás sobre cómo funciona new es JavaScript normal.
Parameter properties
El patrón de arriba (declarar un campo, recibir un parámetro, copiarlo) es tan habitual que TypeScript tiene una forma abreviada. Pon public, private, protected o readonly delante de un parámetro del constructor y se convierte en un campo:
Es una de las pocas características de TypeScript que generan código. El compilador escribe las asignaciones por ti:
class Product {
name;
price;
sku;
constructor(name, price, sku) {
this.name = name;
this.price = price;
this.sku = sku;
}
// ...
}
Como no son simples tipos que se borran, las parameter properties no funcionan con el type stripping integrado de Node (node file.ts falla con ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX), y la opción del compilador erasableSyntaxOnly las marca con el error TS1294. Los proyectos que ejecutan archivos .ts directamente escriben los campos a mano.
Parámetros opcionales y por defecto
Los parámetros del constructor siguen las mismas reglas que los de una función: ? hace uno opcional, un valor por defecto lo hace opcional y lo rellena, y los parámetros obligatorios van primero.
Las parameter properties también pueden tener valores por defecto. El tipo de color dentro de la clase es string | undefined, así que el código que lo usa debe comprobar antes undefined.
Para constructores con muchas opciones, un único objeto de opciones se lee mejor que una larga lista posicional: constructor(opts: { prefix: string; level?: "info" | "debug" }).
Orden de inicialización de los campos
Los inicializadores de campo se ejecutan antes del cuerpo del constructor, y las parameter properties se asignan al principio del cuerpo del constructor. Así que un inicializador de campo no puede leer una parameter property. TypeScript lo detecta:
index.ts(3,18): error TS2729: Property 'size' is used before its initialization.
index.ts(3,30): error TS2729: Property 'size' is used before its initialization.
En tiempo de ejecución el inicializador vería undefined y produciría NaN. Calcula el valor en el cuerpo del constructor:
class Grid {
cells: number;
constructor(public size: number) {
this.cells = size * size;
}
}
console.log(new Grid(3).cells); // 9
Sobrecargas del constructor
Una clase tiene exactamente una implementación del constructor, pero puedes escribir varias firmas de sobrecarga encima. Quien llama solo ve las sobrecargas; la firma de implementación debe ser compatible con todas ellas.
Las sobrecargas se vuelven difíciles de leer a partir de dos o tres formas. Los métodos de fábrica estáticos con nombres descriptivos (Color.fromHex("#ff8800"), Color.fromRgb(10, 20, 30)) suelen ser más claros y no necesitan estrechar tipos dentro de un único cuerpo.
Llamar a super en una subclase
Una clase derivada que declara su propio constructor debe llamar a super(...) con los argumentos del padre, y debe hacerlo antes de tocar this. Olvidar la llamada es el error TS2377 (Constructors for derived classes must contain a 'super' call), y usar this antes es TS17009 ('super' must be called before accessing 'this' in the constructor of a derived class). Los dos reflejan reglas de JavaScript en tiempo de ejecución.
class Animal {
constructor(public name: string) {}
}
class Dog extends Animal {
tricks: string[];
constructor(name: string, ...tricks: string[]) {
super(name); // must come first
this.tricks = tricks;
}
}
const rex = new Dog("Rex", "sit", "roll");
console.log(rex); // Dog { name: 'Rex', tricks: [ 'sit', 'roll' ] }
Una subclase que no añade constructor hereda el del padre, con los mismos tipos de parámetros. Hay más sobre subclases en herencia.
Constructores privados y configuración asíncrona
Un constructor private solo se puede llamar desde dentro de la clase. Fuera, new es el error TS2673. Así obligas a usar una fábrica, para un singleton o para una configuración que tiene que ser asíncrona (los constructores no pueden ser async).
Aquí private es solo una regla en tiempo de compilación. El JavaScript generado tiene un constructor normal, así que código JavaScript normal podría seguir llamando a new Connection(...).
Tipos de constructor
Para aceptar una clase como valor, tipa el parámetro con una firma de construcción: new (...args) => Instance. typeof MyClass también funciona, pero ata el parámetro a la firma del constructor y a los miembros estáticos de esa única clase.
ConstructorParameters e InstanceType extraen la tupla de parámetros y el tipo de instancia de un tipo constructor; se explican junto con las demás funciones auxiliares en ReturnType y Parameters.
Preguntas frecuentes
¿Qué son las parameter properties en TypeScript?
Un parámetro del constructor con un modificador (public, private, protected o readonly) declara un campo y lo asigna en un solo paso. constructor(private name: string) {} es la forma corta de declarar private name: string y escribir this.name = name. El compilador genera esa asignación en la salida.
¿Puede una clase de TypeScript tener varios constructores?
No varias implementaciones. Una clase tiene un solo constructor, pero puedes escribir varias firmas de sobrecarga encima para que quien llama vea listas de parámetros distintas. Los métodos de fábrica estáticos como Color.fromHex() y Color.fromRgb() suelen ser más claros que las sobrecargas.
¿Puede un constructor ser async en TypeScript?
No. Un constructor siempre devuelve la nueva instancia, nunca una promesa, y async constructor() se rechaza con el error TS1089 ('async' modifier cannot appear on a constructor declaration). Usa un constructor privado más un método estático async create() que haga el trabajo asíncrono y después llame a new.
¿Cómo tipo el constructor de una clase como parámetro?
Usa una firma de construcción: new (name: string) => User, o typeof User para esa clase exacta. Una fábrica genérica se ve así: function make<T>(ctor: new () => T): T { return new ctor(); }. ConstructorParameters<typeof User> da la lista de parámetros como una tupla.
¿Por qué me sale "'super' must be called before accessing 'this'"?
Es el error TS17009. En una clase que hace extends de otra, el constructor padre crea el objeto, así que this no existe hasta que se ha ejecutado super(...). Mueve la llamada a super por encima de cualquier línea que use this.