Menu

Domande colloquio TypeScript: 25 domande e risposte con codice

25 domande da colloquio su TypeScript con risposte brevi, corrette e con codice, raggruppate da principiante ad avanzato: any vs unknown, interface vs type, generics, narrowing, utility types, mapped e conditional types, tipizzazione strutturale, tsconfig e TypeScript 7.

Questa pagina include editor eseguibili: modifica, esegui e vedi subito l'output.

Queste sono le domande su TypeScript che i selezionatori fanno più spesso, con la risposta breve che dovresti saper dare e un piccolo esempio. Sono raggruppate per livello; per un ruolo junior di solito ci si ferma al primo gruppo, mentre per uno senior ci si aspetta che tu scriva a memoria i tipi dell'ultimo gruppo.

Domande per principianti

1. Cos'è TypeScript?

TypeScript è JavaScript con tipi statici, creato da Microsoft. Annoti i valori con dei tipi, il compilatore li controlla, poi rimuove i tipi e produce JavaScript semplice che gira ovunque giri JavaScript.

Senza il commento @ts-expect-error, la seconda chiamata è un errore di compilazione, quindi il bug non arriva mai in produzione.

2. TypeScript controlla i tipi a runtime?

No. I tipi vengono cancellati durante la compilazione; nell'output non ne resta traccia. L'esecuzione qui sopra stampa 23 perché niente a runtime sa che a dovrebbe essere un numero. Per controllare dati provenienti dall'esterno del programma (JSON, input dell'utente, risposte di API) scrivi controlli veri, come un type guard o un validatore di schemi.

3. Quali sono i tipi di base?

string, number (un solo tipo per interi e decimali; non esiste int), boolean, bigint, symbol, null e undefined, più gli array (number[]), le tuple ([string, number]), i tipi oggetto, any, unknown, never e void. Usa i nomi in minuscolo: String e Number sono i tipi degli oggetti wrapper.

4. Qual è la differenza tra any e unknown?

Entrambi accettano qualsiasi valore. any in più disattiva il controllo, quindi qualsiasi operazione compila. unknown non permette nulla finché non restringi il tipo, e questo lo rende il tipo sicuro per valori che non hai ancora controllato.

5. Cos'è l'inferenza di tipo?

Il compilatore ricava i tipi che non hai scritto: let count = 0 è un number, const mode = "dark" è il tipo letterale "dark", e il tipo di ritorno di una funzione viene dalle sue istruzioni return. La regola abituale è annotare i parametri delle funzioni e i tipi di ritorno pubblici, e lasciare all'inferenza le variabili locali.

6. Qual è la differenza tra interface e type?

Entrambi descrivono forme di oggetti, e una classe può fare implements di entrambi. Un'interface può essere riaperta e unita (declaration merging) e può fare extends di altre interfacce. Un alias type può dare un nome a qualsiasi cosa: unioni, tuple, primitivi, mapped e conditional types.

interface User { name: string }
interface User { age: number }       // merged: User has name and age

type Id = string | number;           // only a type alias can be a union
type Pair = [string, number];        // or a tuple
type Admin = User & { role: "admin" };

Una convenzione diffusa è interface per le forme di oggetti e type per tutto il resto. Il confronto completo è nella pagina interface vs type.

7. Cosa sono gli union type e gli intersection type?

Un'unione A | B è un valore che è uno dei tipi; puoi usare solo i membri comuni a tutti finché non lo restringi. Un'intersezione A & B è un valore che è entrambi insieme, con tutti i membri di ciascuno.

type Id = string | number;                 // either
type Timestamped = { createdAt: Date };
type Post = { title: string } & Timestamped; // both: title and createdAt

8. Qual è la differenza tra void e never?

void è il tipo di ritorno di una funzione che termina normalmente senza un valore utile. never è il tipo di qualcosa che non può accadere: una funzione che lancia sempre un'eccezione o cicla all'infinito, o un'unione a cui sono stati tolti tutti i casi. never è assegnabile a ogni tipo, e nessun valore è assegnabile a never.

function log(msg: string): void { console.log(msg); }
function fail(msg: string): never { throw new Error(msg); }
type Impossible = string & number; // never

Domande di livello intermedio

9. Cos'è il type narrowing?

Il narrowing è il compilatore che segue i tuoi controlli e raffina un tipo dentro ogni ramo. Capisce typeof, instanceof, in, i controlli di uguaglianza, la truthiness e i type guard definiti dall'utente.

10. Cos'è un type guard definito dall'utente?

Una funzione il cui tipo di ritorno è value is T. Quando restituisce true, la variabile di chi chiama viene ristretta a T. Il compilatore si fida della funzione, quindi il suo corpo deve controllare davvero il valore.

interface Cat { meow(): void }

function isCat(value: unknown): value is Cat {
    return (
        typeof value === "object" &&
        value !== null &&
        "meow" in value &&
        typeof value.meow === "function"
    );
}

Una assertion function, function assertCat(v: unknown): asserts v is Cat, restringe il tipo lanciando un'eccezione invece di restituire false.

11. Cosa sono i generics?

Parametri di tipo che permettono a una funzione, classe o tipo di funzionare con molti tipi mantenendo il legame tra input e output. T viene dedotto dagli argomenti, ed extends vincola ciò che può essere.

pluck(users, "email") sarebbe un errore di compilazione, perché "email" non fa parte di keyof del tipo utente.

12. Cosa fanno keyof e typeof in un tipo?

keyof T è l'unione dei nomi delle proprietà di T. In posizione di tipo, typeof x dà il tipo di una variabile. Insieme, keyof typeof obj trasforma le chiavi di un oggetto in un'unione.

const colors = { red: "#f00", green: "#0f0" };
type Colors = typeof colors;        // { red: string; green: string }
type ColorName = keyof typeof colors; // "red" | "green"

13. Cosa sono gli utility types? Citane alcuni.

Tipi generici integrati che trasformano altri tipi. Quelli chiesti più spesso:

UtilityRisultato
Partial<T>ogni proprietà opzionale
Required<T>ogni proprietà obbligatoria
Readonly<T>ogni proprietà readonly
Pick<T, "a" | "b">solo le proprietà elencate
Omit<T, "a">tutte le proprietà tranne quelle elencate
Record<K, V>un oggetto con chiavi K e valori V
Exclude<U, X>, Extract<U, X>rimuovono o tengono membri di un'unione
NonNullable<T>T senza null e undefined
ReturnType<F>, Parameters<F>i tipi di ritorno o dei parametri di una funzione
Awaited<T>il valore in cui si risolve una promise

L'elenco completo è nella pagina utility types.

14. Cos'è una discriminated union?

Un'unione di tipi oggetto che condividono una proprietà "tag" letterale. Controllare il tag restringe il valore a un solo membro, e un controllo never nel ramo default fa segnalare al compilatore ogni caso che dimentichi.

15. Qual è la differenza tra as e satisfies?

value as T è una type assertion: dice al compilatore di trattare il valore come T e salta gran parte del controllo. value satisfies T controlla il valore rispetto a T ma mantiene il tipo proprio del valore, più preciso.

type Theme = { primary: string; secondary: string };

const a = { primary: "#07f" } as Theme;        // compiles: the missing key is not reported
const b = { primary: "#07f" } satisfies Theme; // error TS2741: Property 'secondary' is missing
const c = {} as { name: string };              // compiles; c.name is undefined at run time

Quando l'oggetto è completo, satisfies mantiene anche il suo tipo dedotto: controllato rispetto a Record<string, string>, la variabile sa ancora esattamente quali chiavi ha, dove un'annotazione la allargherebbe a qualsiasi chiave stringa.

16. Cosa fa l'operatore ! dopo una variabile?

È la non-null assertion: el! rimuove null e undefined dal tipo. Non genera alcun controllo, quindi se il valore è davvero null, il programma va in crash dove viene usato. Preferisci ?., ?? o un if esplicito.

17. Qual è la differenza tra private e #private?

private è fatto rispettare solo dal compilatore; nell'output JavaScript la proprietà è una proprietà normale. #field è un campo privato di JavaScript, fatto rispettare dal runtime.

protected funziona come private ma permette anche l'accesso dalle sottoclassi, e readonly vieta la riassegnazione dopo la costruzione.

18. Qual è la differenza tra una classe astratta e un'interfaccia?

Un'interfaccia è solo un tipo: descrive una forma e sparisce dall'output. Una classe astratta è una classe vera che non può essere istanziata; può contenere metodi implementati, campi e costruttori accanto a membri abstract che le sottoclassi devono implementare. Una classe può implementare molte interfacce ma estendere una sola classe.

abstract class Repository<T> {
    protected items: T[] = [];
    abstract validate(item: T): boolean; // subclasses must implement
    add(item: T): void {                 // shared implementation
        if (this.validate(item)) this.items.push(item);
    }
}

19. Cos'è l'overloading delle funzioni in TypeScript?

Diverse firme di chiamata seguite da un'unica implementazione la cui firma è compatibile con tutte. Chi chiama vede solo gli overload, quindi ogni chiamata ottiene un tipo di ritorno preciso.

function parse(value: string): number;
function parse(value: string[]): number[];
function parse(value: string | string[]): number | number[] {
    return Array.isArray(value) ? value.map(Number) : Number(value);
}

const one = parse("4");          // number
const many = parse(["1", "2"]);  // number[]

Quando il tipo di ritorno non dipende dal tipo dell'argomento, una sola firma con un parametro unione è più semplice.

Domande avanzate

20. Cos'è la tipizzazione strutturale?

TypeScript confronta i tipi in base alla loro forma, non al loro nome. Qualsiasi valore con le proprietà richieste viene accettato, anche se non è mai stato dichiarato con quel tipo.

Il color in più qui va bene perché pixel è una variabile. Passare direttamente l'oggetto letterale, show({ x: 3, y: 4, color: "red" }), è un errore di proprietà in eccesso, un controllo che vale solo per i letterali appena scritti. Quando due tipi con la stessa forma non devono mescolarsi (un UserId e un OrderId che sono entrambi stringhe), usa un branded type: type UserId = string & { readonly __brand: "UserId" }.

21. Come implementeresti da solo Readonly e Partial?

Con i mapped types: itera su keyof T e aggiungi un modificatore a ogni proprietà.

type MyReadonly<T> = { readonly [K in keyof T]: T[K] };
type MyPartial<T> = { [K in keyof T]?: T[K] };
type Mutable<T> = { -readonly [K in keyof T]: T[K] }; // "-" removes a modifier

type User = { name: string; age: number };
type Draft = MyPartial<User>; // { name?: string; age?: number }

22. Cosa sono i conditional types e infer?

T extends U ? X : Y sceglie un tipo in base a una condizione. Dentro la condizione, infer dichiara una variabile di tipo che cattura una parte del tipo corrispondente. I conditional types si distribuiscono sulle unioni.

type MyReturnType<F> = F extends (...args: any[]) => infer R ? R : never;
type ElementOf<T> = T extends (infer E)[] ? E : T;

type A = MyReturnType<() => Promise<number>>; // Promise<number>
type B = ElementOf<string[]>;                 // string
type C = ElementOf<number | boolean[]>;       // number | boolean (distributed)

Altri pattern sono nella pagina conditional types.

23. Cos'è un file .d.ts, e cosa fa declare?

Un file di dichiarazione contiene solo i tipi di codice che esiste altrove, come una libreria JavaScript o le API del browser. declare afferma che un valore esiste senza crearlo: declare const VERSION: string; compila in niente. I tipi delle librerie arrivano inclusi nel pacchetto o dai pacchetti @types/{name}, e tsc genera file .d.ts per il tuo codice con declaration: true.

24. Cosa fa strict, e quali opzioni di tsconfig contano di più?

"strict": true attiva noImplicitAny, strictNullChecks, strictFunctionTypes, strictBindCallApply, strictPropertyInitialization, noImplicitThis, useUnknownInCatchVariables e strictBuiltinIteratorReturn. In TypeScript 7 è attivo per impostazione predefinita. Le altre opzioni su cui fanno domande:

OpzioneRisposta in una riga
targetquale versione di JavaScript usa l'output
moduleil formato dei moduli in output: nodenext segue le regole di Node, esnext/preserve mantengono import per un bundler
moduleResolutioncome vengono trovati gli import: nodenext vuole ./file.js nei file ES module, bundler accetta ./file
noUncheckedIndexedAccessarr[i] include undefined nel suo tipo
pathsalias di import solo per il type checker; l'output mantiene l'alias, quindi deve risolverlo un bundler o il campo imports di Node
skipLibChecksalta il controllo dei file .d.ts, per velocità
verbatimModuleSyntaxgli import usati solo come tipi vanno marcati type; gli altri import restano come sono scritti

25. Cos'è TypeScript 7?

Il compilatore di TypeScript riscritto in Go come programma nativo, pubblicato come versione 7 del pacchetto npm typescript. Il comando è sempre tsc e il linguaggio è lo stesso, ma è molto più veloce del compilatore 6.x basato su JavaScript: il team di TypeScript riporta accelerazioni da 8x a 12x su build complete tipiche, grazie al codice nativo e al multithreading a memoria condivisa. Rende anche effettive le rimozioni che la 6.0 aveva deprecato; per esempio baseUrl, outFile, moduleResolution: "node10", module: "amd" e alwaysStrict: false ora sono errori (TS5102, TS5108) che ti dicono di rimuoverli.

Domande frequenti

Quali sono le domande più comuni ai colloqui su TypeScript?

Quelle che escono più spesso: la differenza tra any e unknown, interface vs type, come funzionano i generics, come funzionano il narrowing e i type guard, cosa fanno utility types come Partial, Pick e Omit, e se TypeScript controlla i tipi a runtime (non lo fa).

Quali domande su TypeScript si fanno agli sviluppatori senior?

Aspettati di dover scrivere tipi, non solo leggerli: implementare Readonly o ReturnType con mapped e conditional types, spiegare la tipizzazione strutturale e quando usare i branded types, progettare una discriminated union con un controllo di esaustività, e discutere scelte di tsconfig come strict, noUncheckedIndexedAccess e la risoluzione dei moduli.

Come ci si prepara a un colloquio su TypeScript?

Scrivi piccoli programmi con strict attivo e leggi gli errori del compilatore finché non riesci a prevederli. Devi saper spiegare il narrowing, i generics con vincoli, i principali utility types e perché le type assertion non sono sicure, ciascuno con un esempio di due righe.

TypeScript viene chiesto nei colloqui React e Angular?

Di solito sì. Angular stesso è scritto in TypeScript e le app Angular si scrivono in TypeScript, quindi i colloqui Angular tendono a darlo per scontato, compresi decorator come @Component e i modificatori di accesso. I colloqui React per una codebase TypeScript chiedono spesso di tipizzare props, state e gestori di eventi, che usano le stesse interfacce, unioni e generics mostrati qui.

Illustrazione dei linguaggi di programmazione di Coddy

Impara a programmare con Coddy

INIZIA