Un costruttore in TypeScript è il metodo constructor della classe con parametri tipizzati. Non ha un'annotazione del tipo di ritorno: produce sempre un'istanza della classe.
Scrivere constructor(): Product è l'errore TS1093 (Type annotation cannot appear on a constructor declaration). Tutto il resto del funzionamento di new è JavaScript normale.
Parameter property
Il pattern qui sopra (dichiarare un campo, ricevere un parametro, copiarlo) è così comune che TypeScript ha una forma breve. Metti public, private, protected o readonly prima di un parametro del costruttore e diventa un campo:
È una delle poche funzionalità di TypeScript che genera codice. Il compilatore scrive le assegnazioni al posto tuo:
class Product {
name;
price;
sku;
constructor(name, price, sku) {
this.name = name;
this.price = price;
this.sku = sku;
}
// ...
}
Dato che non si tratta solo di tipi cancellati, le parameter property non funzionano con il type stripping integrato di Node (node file.ts fallisce con ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX), e l'opzione del compilatore erasableSyntaxOnly le segnala come errore TS1294. I progetti che eseguono direttamente i file .ts scrivono i campi a mano.
Parametri facoltativi e predefiniti
I parametri del costruttore seguono le stesse regole dei parametri delle funzioni: ? ne rende uno facoltativo, un valore predefinito lo rende facoltativo e lo riempie, e i parametri obbligatori vengono prima.
Anche le parameter property possono avere valori predefiniti. Il tipo di color dentro la classe è string | undefined, quindi il codice che lo usa deve prima controllare undefined.
Per i costruttori con molte opzioni, un unico oggetto di opzioni si legge meglio di un lungo elenco posizionale: constructor(opts: { prefix: string; level?: "info" | "debug" }).
Ordine di inizializzazione dei campi
Gli inizializzatori dei campi vengono eseguiti prima del corpo del costruttore, e le parameter property vengono assegnate all'inizio del corpo del costruttore. Quindi l'inizializzatore di un campo non può leggere una parameter property. TypeScript lo intercetta:
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.
A runtime l'inizializzatore vedrebbe undefined e produrrebbe NaN. Calcola invece il valore nel corpo del costruttore:
class Grid {
cells: number;
constructor(public size: number) {
this.cells = size * size;
}
}
console.log(new Grid(3).cells); // 9
Overload del costruttore
Una classe ha esattamente un'implementazione del costruttore, ma puoi elencare sopra di essa diverse firme di overload. Chi chiama vede solo gli overload; la firma dell'implementazione deve essere compatibile con tutti.
Gli overload diventano difficili da leggere oltre due o tre forme. I metodi factory statici con nomi descrittivi (Color.fromHex("#ff8800"), Color.fromRgb(10, 20, 30)) sono spesso più chiari e non richiedono di restringere i tipi dentro un unico corpo.
Chiamare super in una sottoclasse
Una classe derivata che dichiara il proprio costruttore deve chiamare super(...) con gli argomenti del genitore, e deve farlo prima di toccare this. Dimenticare la chiamata è l'errore TS2377 (Constructors for derived classes must contain a 'super' call), e usare this prima è TS17009 ('super' must be called before accessing 'this' in the constructor of a derived class). Entrambi rispecchiano regole del runtime JavaScript.
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 sottoclasse che non aggiunge un costruttore eredita quello del genitore, con gli stessi tipi di parametro. Altro sulle sottoclassi in ereditarietà.
Costruttori privati e inizializzazione asincrona
Un costruttore private si può chiamare solo dall'interno della classe. Da fuori, new è l'errore TS2673. È così che obblighi chi chiama a passare da una factory, per un singleton o per un'inizializzazione che deve essere asincrona (i costruttori non possono essere async).
Qui private è solo una regola di compilazione. Il JavaScript emesso ha un costruttore normale, quindi del semplice codice JavaScript potrebbe comunque chiamare new Connection(...).
Tipi costruttore
Per accettare una classe come valore, tipizza il parametro con una firma di costruzione: new (...args) => Instance. Funziona anche typeof MyClass, ma lega il parametro alla firma del costruttore e ai membri statici di quella sola classe.
ConstructorParameters e InstanceType estraggono la tupla dei parametri e il tipo dell'istanza da un tipo costruttore; sono trattati insieme agli altri helper per le funzioni in ReturnType e Parameters.
Domande frequenti
Cosa sono le parameter property in TypeScript?
Un parametro del costruttore con un modificatore (public, private, protected o readonly) dichiara un campo e lo assegna in un solo passaggio. constructor(private name: string) {} è la forma breve per dichiarare private name: string e scrivere this.name = name. Il compilatore genera quell'assegnazione nell'output.
Una classe TypeScript può avere più costruttori?
Non più implementazioni. Una classe ha un solo costruttore, ma puoi scrivere sopra di esso diverse firme di overload così chi chiama vede elenchi di parametri distinti. I metodi factory statici come Color.fromHex() e Color.fromRgb() sono spesso più chiari degli overload.
Un costruttore può essere async in TypeScript?
No. Un costruttore restituisce sempre la nuova istanza, mai una promise, e async constructor() viene rifiutato con l'errore TS1089 ('async' modifier cannot appear on a constructor declaration). Usa un costruttore privato più un metodo statico async create() che svolge il lavoro asincrono e poi chiama new.
Come si tipizza il costruttore di una classe passato come parametro?
Usa una firma di costruzione: new (name: string) => User, oppure typeof User per quella classe esatta. Una factory generica ha questo aspetto: function make<T>(ctor: new () => T): T { return new ctor(); }. ConstructorParameters<typeof User> dà l'elenco dei parametri come tupla.
Perché ricevo "'super' must be called before accessing 'this'"?
È l'errore TS17009. In una classe che fa extends di un'altra, è il costruttore genitore a creare l'oggetto, quindi this non esiste finché super(...) non è stato eseguito. Sposta la chiamata a super sopra qualsiasi riga che tocchi this.