Classi e modificatori di accesso in TypeScript

TypeScript estende le classi già presenti in JavaScript aggiungendo funzionalità dedicate alla tipizzazione e al controllo dell'accesso alle proprietà e ai metodi.

Possiamo quindi specificare il tipo dei campi di una classe, dei parametri del costruttore e dei valori restituiti dai metodi. Inoltre, TypeScript mette a disposizione i modificatori di visibilità public, private e protected, che permettono di stabilire da quali parti del codice è possibile accedere ai membri di una classe.

Definire una classe in TypeScript

La sintassi di una classe TypeScript è simile a quella di JavaScript:

class Persona {
    nome: string;
    eta: number;

    constructor(nome: string, eta: number) {
        this.nome = nome;
        this.eta = eta;
    }
}

In questo esempio:

  • nome è un campo di tipo string,
  • eta è un campo di tipo number.
  • Il costruttore riceve gli stessi tipi di dati.
  • this.nome e this.eta inizializzano i campi dell'istanza.

Possiamo quindi creare un oggetto della classe:

const persona = new Persona("Andrea", 30);

TypeScript controlla che gli argomenti passati al costruttore siano compatibili con i tipi dichiarati:

const persona = new Persona("Andrea", "30"); // Errore

In questo caso viene segnalato un errore perché eta richiede un number.

Tipizzare i metodi

Anche i metodi possono avere parametri e valori di ritorno tipizzati. Ad esempio:

class Persona {
    nome: string;

    constructor(nome: string) {
        this.nome = nome;
    }

    saluta(): string {
        return `Ciao, sono ${this.nome}`;
    }
}

Il metodo saluta() non riceve parametri e restituisce una string.

Possiamo anche lasciare che TypeScript determini automaticamente il tipo di ritorno quando questo può essere dedotto facilmente:

class Persona {
    nome: string;

    constructor(nome: string) {
        this.nome = nome;
    }

    saluta() {
        return `Ciao, sono ${this.nome}`;
    }
}

In questo caso TypeScript deduce automaticamente che il metodo restituisce una string.

Modificatori di visibilità

TypeScript mette a disposizione 3 diversi modificatori di visibilità: public, private e protected.

Visibilità pubblica

Il modificatore public indica che un membro della classe può essere utilizzato da qualsiasi parte del codice.

Ad esempio:

class Persona {
    public nome: string;

    constructor(nome: string) {
        this.nome = nome;
    }
}

Possiamo accedere alla proprietà dall'esterno:

const persona = new Persona("Andrea");

console.log(persona.nome);

public è il comportamento predefinito dei membri delle classi TypeScript. Per questo motivo possiamo anche ometterlo.

Visibilità privata

Il modificatore private limita l'accesso a un membro della classe.

Ad esempio:

class Conto {
    private saldo: number;

    constructor(saldo: number) {
        this.saldo = saldo;
    }
}

Il campo saldo può essere utilizzato all'interno della classe:

class Conto {
    private saldo: number;

    constructor(saldo: number) {
        this.saldo = saldo;
    }

    mostraSaldo(): number {
        return this.saldo;
    }
}

Ma non possiamo accedervi direttamente dall'esterno:

const conto = new Conto(1000);
console.log(conto.saldo); // Errore

TypeScript segnala un errore perché saldo è private.

Possiamo invece utilizzare il metodo pubblico:

console.log(conto.mostraSaldo());

In questo modo la classe controlla come il proprio stato interno viene esposto all'esterno.

Visibilità protetta

La visibilità protected è simile a private, ma permette l'accesso anche alle classi che estendono quella originale.

Per esempio:

class Persona {
    protected nome: string;

    constructor(nome: string) {
        this.nome = nome;
    }
}

Una classe derivata può utilizzare nome:

class Studente extends Persona {
    mostraNome(): string {
        return this.nome;
    }
}

Possiamo quindi scrivere:

const studente = new Studente("Andrea");
console.log(studente.mostraNome());

Tuttavia, nome non è accessibile direttamente dall'esterno (ad esempio, usando studente.nome).

Inizializzazione abbreviata nel costruttore

TypeScript offre una sintassi abbreviata per dichiarare e inizializzare contemporaneamente i campi della classe.

In questo esempio, possiamo utilizzare direttamente i modificatori nei parametri del costruttore:

class Persona {
    constructor(
        public nome: string,
        public eta: number
    ) {}
}

L'esempio abbrevia questa scrittura più lunga:

class Persona {
    nome: string;
    eta: number;

    constructor(nome: string, eta: number) {
        this.nome = nome;
        this.eta = eta;
    }
}

Infatti, TypeScript crea automaticamente i campi e assegna loro i valori ricevuti dal costruttore.

Possiamo quindi creare normalmente un'istanza:

const persona = new Persona("Andrea", 30);

console.log(persona.nome);
console.log(persona.eta);

Questa sintassi è chiamata, in gergo, parameter property e permette di ridurre notevolmente il codice necessario per inizializzare i campi.

Campi immutabili di una classe

Il modificatore readonly può essere applicato ai campi di una classe per impedire che vengano riassegnati dopo la loro inizializzazione.

Per esempio:

class Persona {
    readonly id: number;
    nome: string;

    constructor(id: number, nome: string) {
        this.id = id;
        this.nome = nome;
    }
}

Possiamo modificare nome, ma non possiamo riassegnare id:

const persona = new Persona(1, "Andrea");

persona.nome = "Luca";
persona.id = 2; // Errore

TypeScript segnala un errore perché id è readonly.

Un campo readonly può quindi essere assegnato durante l'inizializzazione, ad esempio nel costruttore, ma non successivamente.


Possiamo combinare readonly con la sintassi abbreviata che abbiamo visto precedentemente:

class Persona {
    constructor(
        public readonly id: number,
        public nome: string
    ) {}
}

Prova!Completa gli spazi vuoti con il testo appropriato.
// Impedisci l'accesso alla proprietà dall'esterno della classe
class Veicolo {
    targa: string;

    constructor(targa: string) {
        this.targa = targa;
    }
}

Prova! Scegli l'opzione corretta tra quelle elencate.
class ContoBancario {
    constructor(private saldo: number) {}

    getSaldo(): number {
        return this.saldo;
    }
}

const conto = new ContoBancario(500);
console.log(____________); // 500