Tipi avanzati di TypeScript spiegati con esempi

TypeScript offre diversi tipi avanzati che vanno oltre i tipi di base, consentendo sistemi di tipi più flessibili e potenti. Questi tipi avanzati aiutano a creare applicazioni robuste fornendo modi aggiuntivi per definire e applicare vincoli di tipo. Questo articolo esplora alcuni di questi tipi avanzati con esempi.

Tipi di unione

I tipi di unione consentono a una variabile di essere di uno di diversi tipi. Ciò può essere utile quando un valore potrebbe essere di più tipi ma dovrebbe essere gestito in modo appropriato in base al suo tipo effettivo.

// Union type example

function formatValue(value: string | number): string {
  if (typeof value === 'string') {
    return `String: ${value}`;
  } else {
    return `Number: ${value.toFixed(2)}`;
  }
}

console.log(formatValue("Hello"));
console.log(formatValue(123.456));

In questo esempio, la funzione `formatValue` accetta una stringa o un numero e formatta il valore di conseguenza.

Tipi di intersezione

I tipi di intersezione combinano più tipi in uno. Un oggetto di un tipo di intersezione avrà tutte le proprietà dei tipi combinati. Ciò è utile per comporre più tipi insieme.

// Intersection type example

interface Person {
  name: string;
  age: number;
}

interface Contact {
  email: string;
  phone: string;
}

type Employee = Person & Contact;

const employee: Employee = {
  name: "John Doe",
  age: 30,
  email: "[email protected]",
  phone: "123-456-7890"
};

console.log(employee);

Qui, il tipo `Employee` è un'intersezione di `Person` e `Contact`, il che significa che contiene proprietà di entrambe le interfacce.

Tipi letterali

I tipi letterali specificano i valori esatti che una variabile può contenere. Ciò può essere particolarmente utile per garantire che siano consentiti solo determinati valori specifici.

// Literal type example

type Direction = "up" | "down" | "left" | "right";

function move(direction: Direction): void {
  console.log(`Moving ${direction}`);
}

move("up");    // Valid
move("down");  // Valid
// move("side"); // Error: Argument of type '"side"' is not assignable to parameter of type 'Direction'.

In questo caso, il tipo `Direction` è limitato a quattro valori stringa specifici, garantendo che solo queste direzioni possano essere utilizzate nella funzione `move`.

Tipi di tupla

I tipi di tupla rappresentano un array con un numero fisso di elementi in cui ogni elemento può avere un tipo diverso. Le tuple sono utili per rappresentare collezioni di dimensioni fisse di elementi eterogenei.

// Tuple type example

let user: [string, number] = ["Alice", 30];

console.log(user[0]); // "Alice"
console.log(user[1]); // 30

// user = [30, "Alice"]; // Error: Type 'number' is not assignable to type 'string'.

La tupla `user` è definita da una stringa seguita da un numero e questa struttura deve essere mantenuta.

Tipi condizionali

I tipi condizionali consentono di determinare i tipi in base a condizioni. Forniscono un modo per selezionare un tipo o un altro in base a una condizione.

// Conditional type example

type IsString = T extends string ? "Yes" : "No";

type Test1 = IsString;  // "Yes"
type Test2 = IsString;  // "No"

In questo esempio, il tipo `IsString` controlla se un tipo `T` è una stringa. Restituisce `"Yes"` se lo è e `"No"` in caso contrario.

Tipi mappati

I tipi mappati consentono di creare nuovi tipi trasformando le proprietà di un tipo esistente. Ciò è utile per modificare o estendere i tipi esistenti.

// Mapped type example

type ReadonlyPerson = {
  readonly [K in keyof Person]: Person[K];
};

const readonlyPerson: ReadonlyPerson = {
  name: "Alice",
  age: 30
};

// readonlyPerson.age = 31; // Error: Cannot assign to 'age' because it is a read-only property.

Il tipo `ReadonlyPerson` trasforma il tipo `Person` rendendo tutte le sue proprietà di sola lettura.

Conclusione

I tipi avanzati di TypeScript forniscono potenti strumenti per definire e gestire requisiti di tipo complessi. Utilizzando tipi union, intersection, literal, tuple, conditional e mapped, gli sviluppatori possono creare applicazioni più robuste e gestibili. La comprensione e l'applicazione efficace di questi tipi può migliorare significativamente la sicurezza dei tipi e la flessibilità del codice TypeScript.