Menu

any vs unknown in TypeScript: differenze e quando usarli

Sia any sia unknown accettano qualsiasi valore. any disattiva il controllo dei tipi per quel valore, mentre unknown ti obbliga a verificarlo prima di usarlo. Scopri le differenze, come restringere unknown, noImplicitAny e dove any si infila nel codice tipizzato.

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

any e unknown accettano entrambi qualsiasi valore. La differenza sta in cosa puoi fare con il valore dopo: any ti lascia fare tutto e non controlla nulla, mentre unknown non ti lascia fare quasi nulla finché non dimostri cos'è il valore.

Senza la riga @ts-expect-error, u.toUpperCase() è l'errore di compilazione TS18046. Il controllo typeof restringe u a string, e dentro quel blocco tutti i metodi delle stringhe sono disponibili.

any vs unknown in breve

anyunknown
Accetta qualsiasi valoresìsì
Assegnarlo a string, number, ...sì, senza controllino (TS2322)
Leggere una proprietà, chiamare un metodosì, senza controllino (TS18046)
Chiamarlo come funzionesìno
Aritmetica e confronti (x * 2, x + 1, x < 5)sìno (TS18046)
Serve un controllo prima dell'usonosì (typeof, instanceof, in, una type guard)
Effetto sul controllo dei tipidisattivato per quel valore e tutto ciò che toccaresta attivo

In termini di teoria dei tipi, unknown è il top type: ogni tipo gli è assegnabile, e lui è assegnabile solo a unknown e any. any è una via di fuga assegnabile in entrambe le direzioni, a tutto tranne che a never.

any disattiva il controllo dei tipi

Un valore tipizzato any riceve fiducia cieca. Il compilatore accetta errori di battitura, tipi sbagliati e proprietà mancanti, e gli errori saltano fuori a runtime.

L'output mostra il problema: una variabile annotata number contiene una stringa, e l'ultima riga lancia Cannot read properties of undefined (reading 'city'). Inoltre any si propaga. user.name è any, quindi ogni valore calcolato da lì è any, e un solo valore non tipizzato può disattivare i controlli lontano dal punto in cui è entrato.

unknown ti obbliga a controllare prima

Con unknown, il compilatore rifiuta ogni operazione finché il codice non restringe il valore. Per restringere si usano normali controlli JavaScript, e dentro ogni ramo il valore ha il tipo verificato.

Il controllo value === null deve venire prima di quello sull'oggetto perché typeof null è "object". Dopo "id" in value, TypeScript sa che l'oggetto ha una proprietà id, tipizzata unknown, perché nulla dice cosa contiene. String() la trasforma esplicitamente in testo; per usarla come numero la controlleresti prima con typeof.

Puoi anche saltare il controllo con un'asserzione, value as string, e il compilatore la accetta. È una promessa senza alcun controllo a runtime dietro, quindi preferisci un controllo vero; vedi le type assertion.

Validare JSON con unknown

JSON.parse è dichiarato con ritorno any, quindi il suo risultato disattiva i controlli in silenzio. Annota il risultato come unknown e scrivi una type guard che verifichi la forma prima che il resto del codice se ne fidi.

Il primo input stampa dark at 14px, il secondo viene rifiutato perché manca fontSize. Con any, il secondo input sarebbe passato come Settings con fontSize undefined. Per schemi grandi, una libreria di validazione fa lo stesso lavoro e ricava il tipo al posto tuo.

noImplicitAny

any non arriva solo da chi lo scrive. Anche un parametro senza annotazione e senza contesto da cui dedurre il tipo sarebbe any. L'opzione noImplicitAny, attivata da strict, lo segnala come errore:

index.ts(2,17): error TS7006: Parameter 'x' implicitly has an 'any' type.

La soluzione è un'annotazione, function double(x: number). Anche scrivere esplicitamente x: any compila, ed è proprio questo il punto: any resta visibile nel codice e si può cercare e revisionare.

Dove any si infila ancora

Anche con strict, queste fonti producono any senza che la parola compaia nel tuo codice:

FonteCosa ottieniCosa fare
JSON.parse(text)anyannotalo come unknown, poi validalo
response.json() con i tipi del browser (DOM)Promise<any>come per JSON.parse (i tipi del fetch di Node restituiscono già Promise<unknown>)
Un pacchetto senza definizioni di tipoany per i suoi import (con un errore se non aggiungi una dichiarazione)installa @types/... o scrivi un .d.ts
value as anyanyusa una type guard o un'asserzione precisa
catch (e) con useUnknownInCatchVariables disattivatoanystrict lo rende unknown; lascialo così

La variabile del catch è unknown con strict perché si può lanciare qualsiasi cosa, non solo oggetti Error. Restringila con e instanceof Error prima di leggere e.message.

Quando any è accettabile

any non è vietato, ma ognuno è un punto che il compilatore non protegge più. Usi ragionevoli:

  • Migrare un codebase JavaScript, dove any segna ciò che non è ancora tipizzato.
  • Codice che il sistema di tipi non esprime bene, tenuto piccolo e dietro la firma di una funzione tipizzata.
  • Codice di test che passa di proposito dati sbagliati.

Per tutto il resto, unknown copre lo stesso caso "non conosco questo tipo" mantenendo i controlli. Molti team lo impongono con la regola di lint @typescript-eslint/no-explicit-any. Un tipo correlato, Record<string, unknown>, è la scelta abituale per "un oggetto qualsiasi con valori sconosciuti".

Domande frequenti

Che differenza c'è tra any e unknown in TypeScript?

Entrambi accettano qualsiasi valore. Con any puoi fare qualsiasi cosa con il valore (leggere proprietà, chiamarlo, assegnarlo a un number) e il compilatore non controlla nulla. Con unknown non puoi fare quasi nulla finché non lo restringi con un controllo come typeof x === "string". unknown lascia attivo il controllo dei tipi, ed è per questo che è la scelta più sicura.

Quando usare unknown invece di any?

Ogni volta che il tipo di un valore non è noto in compilazione: JSON analizzato, dati da una risposta di rete, un errore catturato, l'input di una funzione di validazione. Tipizzalo come unknown e restringilo. Ricorri ad any solo per lavori di migrazione temporanei o per codice che il sistema di tipi non riesce a descrivere.

Cosa significa "Object is of type 'unknown'"?

Gli errori TS18046 ('x' is of type 'unknown') e TS2571 (Object is of type 'unknown', usato quando il valore non è un semplice nome, come load().id) indicano che hai usato un valore unknown come se avesse un tipo preciso, per esempio leggendo una proprietà o chiamando un metodo. Controlla prima il tipo (typeof, instanceof, Array.isArray, in o una funzione type guard) e usa il valore dentro il ramo ristretto.

Perché JSON.parse restituisce any?

La sua dichiarazione nella libreria standard dice parse(text: string, ...): any, perché il compilatore non può sapere cosa contiene una stringa. Il risultato disattiva in silenzio il controllo per tutto ciò che tocca. Annotalo invece: const data: unknown = JSON.parse(text), poi validalo prima di usarlo.

Che cos'è noImplicitAny?

Un'opzione del compilatore, parte di strict, che segnala un errore quando una dichiarazione riceverebbe in silenzio il tipo any perché non ha annotazioni e niente da cui dedurlo: TS7006 per un parametro, TS7005 o TS7034 per una variabile il cui tipo non si può ricavare. Impedisce che any compaia senza che nessuno lo scriva. Un any esplicito resta consentito.

Illustrazione dei linguaggi di programmazione di Coddy

Impara a programmare con Coddy

INIZIA