I decoratori in TypeScript sono un modo pratico e semplice per assegnare funzionalità aggiuntive agli oggetti senza modificare il codice sorgente. È possibile utilizzarli su classi, metodi, proprietà, funzioni di accesso e parametri, così da accedere ad annotazioni e metadati.

Che cosa sono i decoratori di TypeScript e a cosa servono?

Il principio dei decoratori di TypeScript non è del tutto nuovo. In altri linguaggi di programmazione sono disponibili funzionalità simili, ad esempio gli attributi in C#, i decoratori in Python o le annotazioni in Java. Si tratta di possibilità per espandere le funzionalità di un oggetto senza modificare il codice sorgente. Anche TypeScript sfrutta questo principio già da tempo. Anche se la maggior parte dei browser non supporta (ancora) i decoratori di TypeScript, vale comunque la pena sperimentare questo approccio e le sue possibilità. A partire dalla versione 5.0, l’uso dei decoratori è stato notevolmente semplificato.

I decoratori di TypeScript sono utilizzati per creare annotazioni e metadati aggiuntivi per le classi di TypeScript e per aggiungere elementi. Oltre alle classi è possibile modificare anche metodi, proprietà, metodi di accesso e parametri. Questi ultimi possono essere verificati ed è possibile richiamarne i valori. Anche questa è una grande differenza tra i decoratori di TypeScript e il loro equivalente per JavaScript.

Managed Nextcloud di IONOS Cloud
Lavora con il tuo team sul cloud
  • Massima sicurezza dei tuoi dati
  • Strumenti di collaborazione per lavorare in team
  • Aggiornamenti automatici

Sintassi e funzionamento dei decoratori

Quando aggiungi i decoratori di TypeScript a un oggetto, in realtà stai richiamando una funzione che può essere eseguita senza modificare il codice sorgente. Ciò aumenta la funzionalità e preserva comunque la chiarezza del codice. La sintassi di base si presenta così:

@nomeDecoratore
typescript

È possibile creare questa funzione con due o tre parametri. La sintassi per la funzione con tre parametri si presenta simile a questa:

function FunzioneDecoratore(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
    console.log(`Decorating ${propertyKey} of class ${target.constructor.name}`);
}
class MyClass {
    @FunzioneDecoratore
    myMethod() {
    }
}
typescript

I singoli componenti dei decoratori di TypeScript sono costituiti da questi elementi:

  • target: target indica l’oggetto a cui è stato assegnato il decoratore
  • propertyKey: propertyKey è una stringa contenente il nome della classe a cui è stato assegnato un decoratore; tra le varie opzioni, è possibile inserire metodi o proprietà
  • descriptor: in descriptor è possibile inserire ulteriori informazioni sull’oggetto a cui si applica il decoratore; le proprietà possibili sono value, writable, enumerable o configurable.

La sintassi dei decoratori in TypeScript con due parametri è la seguente:

function FunzioneDecoratore(target: any) {
    console.log(`Decorating ${target.name}`);
}
@FunzioneDecoratore
class MyClass {
}
typescript

In questo caso, i decoratori di TypeScript sono stati applicati a una classe.

I diversi tipi di decoratori

Esistono diversi tipi di decoratori di TypeScript, ciascuno dotato di caratteristiche particolari e che descriveremo in dettaglio più avanti nell’articolo:

  • Decoratori di classe
  • Decoratori di metodo
  • Decoratori di proprietà
  • Decoratori di accessor
  • Decoratori di parametri

Decoratori di TypeScript per classi

I decoratori di TypeScript ti permettono di personalizzare le proprietà di una classe e modificarne il costruttore, i metodi o le proprietà. Come primo parametro accettano il costruttore non appena si “decora” la classe con una funzione. Quello che segue è un codice di esempio in cui lavoriamo con un elenco di clienti. Qui si possono notare alcune proprietà private e altre pubbliche:

class Clienti {
    private static userType: string = "Generic";
    private _email: string;
    public nomecliente: string;
    public via: string = "";
    public località: string = "";
    public paese: string = "";
    constructor(nomecliente: string, email: string) {
        this.nomecliente = nomecliente;
        this._email = email;
    }
    Static get userType() {
        return Clienti.userType;
    }
    get email() {
        return this._email;
    }
    set email(emailnuova: string) {
        this._email = emailnuova;
    }
    indirizzo(): string {
        return `${this.via}\n${this.località}\n${this.paese}`;
    }
}
const p = new Clienti("esempiocliente", "nome@esempio.com");
p.via = "Via Garibaldi 2";
p.località = "Roma";
typescript

Nel passaggio successivo utilizzeremo quindi i decoratori di TypeScript per aggiungere funzioni supplementari che tuttavia non vanno poi a modificare il codice sorgente. Aggiungiamo quindi il decoratore @frozen per la classe “Clienti”. Questa funzione fa sì che gli oggetti non possano essere modificati successivamente. Per alcune proprietà utilizziamo @required per dare istruzioni esplicite all’input. Utilizziamo inoltre @enumerable per gli elenchi e @deprecated per gli input obsoleti. Per prima cosa ci occupiamo della definizione dei decoratori:

function frozen(constructor: Function) {
    Object.freeze(constructor);
    Object.freeze(constructor.prototype);
}
function required(target: any, propertyKey: string) {
    // Logica per il decoratore Required
}
function enumerable(value: boolean) {
    return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
        descriptor.enumerable = value;
    };
}
function deprecated(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
    console.warn(`The method ${propertyKey} is deprecated.`);
}
typescript

Dopo l’utilizzo dei decoratori di TypeScript, il codice finale si presenta così:

@frozen
class Clienti {
    private static userType: string = "Generic";
    @required
    private _email: string;
    @required
    public nomecliente: string;
    public via: string = "";
    public località: string = "";
    public paese: string = "";
    constructor(nomecliente: string, email: string) {
        this.nomecliente = nomecliente;
        this._email = email;
    }
    @enumerable(false)
    get userType() {
        return Clienti.userType;
    }
    get email() {
        return this._email;
    }
    set email(emailnuova: string) {
        this._email = emailnuova;
    }
    @deprecated
    indirizzo(): string {
        return `${this.via}\n${this.località}\n${this.paese}`;
    }
}
const p = new Clienti("esempiocliente", "nome@esempio.com");
p.via = "Via Garibaldi, 2";
p.località = "Roma";
typescript

Decoratori di TypeScript per metodi

È possibile utilizzare i decoratori di TypeScript anche per i metodi. Fanno eccezione solo i file di dichiarazione, l’overloading e la classe “declare”. Nell’esempio seguente utilizziamo @enumerable come decoratore per il metodo getName nella classe “Person”:

const enumerable = (value: boolean) => {
    return (target: any, propertyKey: string, propertyDescriptor: PropertyDescriptor) => {
        propertyDescriptor.enumerable = value;
    }
}
class Person {
    nome: string = "Giulia"
    cognome: string = "Rossi"
    @enumerable(true)
    getName () {
        return `${this.nome} ${this.cognome}`;
    }
}
typescript

Decoratori di TypeScript per proprietà

I decoratori di TypeScript per le proprietà di una classe (decoratori di proprietà) possono avere due parametri: la funzione costruttore della classe e il nome della proprietà. Nell’esempio seguente utilizziamo il decoratore per restituire il nome di una proprietà (in questo caso il nome del cliente):

const printPropertyName = (target: any, propertyName: string) => {
    console.log(propertyName);
};
class Clienti {
    @printPropertyName
    name: string = "Giulia";
}
typescript

Decoratori di TypeScript per funzioni di accesso

I decoratori di accessor funzionano secondo un principio molto simile ai decoratori di proprietà. Rispetto a questi ultimi hanno un terzo parametro aggiuntivo. Nel nostro esempio, questo parametro è il descrittore di proprietà per un cliente. A questo punto, se si restituisce un valore utilizzando il decoratore di accessor, il valore diventa il nuovo descrittore della proprietà. Il codice seguente cambia quindi il valore booleano (“true” o “false”) di enumerable. Il nostro punto di partenza è il seguente:

const enumerable = (value: boolean) => {
    return (target: any, propertyKey: string, descriptor: PropertyDescriptor) => {
        descriptor.enumerable = value;
    }
}
typescript

Il decoratore si utilizza con questo codice:

class Clienti {
    nome: string = "Giulia";
    cognome: string = "Rossi";
    @enumerable(true)
    get name() {
        return `${this.nome} ${this.cognome}`;
    }
}
typescript

Decoratori di TypeScript per parametri

Anche i decoratori di parametri in TypeScript presentano tre parametri: la funzione di costruttore della classe, il nome del metodo e un nome di indice per il parametro. Tuttavia, il parametro stesso non può essere modificato: pertanto questo decoratore può essere utilizzato solo a scopo di verifica. Se, ad esempio, intendi interrogare l’indice, puoi farlo con questo codice:

function print(target: Object, propertyKey: string, parameterIndex: number) {
    console.log(`Decorating param ${parameterIndex} from ${propertyKey}`);
}
typescript

Se poi vuoi applicare il decoratore di parametro, il codice è questo:

class Esempio {
    testMethod(param0: any, @print param1: any) {}
}
typescript
Consiglio

L’ideale per i siti web statici e per le app: Deploy Now di IONOS mette a tua disposizione un facile staging, una configurazione veloce e flussi di lavoro perfettamente coordinati. Trova il modello giusto per le tue esigenze!

Hai trovato questo articolo utile?
Vai al menu principale