Menu

TypeScript any vs unknown: Unterschiede und wann was

any und unknown akzeptieren beide jeden Wert. any schaltet die Typprüfung für diesen Wert ab, während unknown dich zwingt, den Wert vor der Verwendung zu prüfen. Die Unterschiede, wie du unknown einengst, noImplicitAny und wo sich any in typisierten Code schleicht.

Diese Seite enthält ausführbare Editoren - bearbeiten, ausführen und Ausgabe sofort sehen.

any und unknown akzeptieren beide jeden Wert. Der Unterschied liegt darin, was du danach mit dem Wert tun kannst: any lässt dich alles tun und prüft nichts, während unknown dich fast nichts tun lässt, bis du beweist, was der Wert ist.

Ohne die Zeile mit @ts-expect-error ist u.toUpperCase() der Compilerfehler TS18046. Die Prüfung mit typeof engt u auf string ein, und in diesem Block ist jede String-Methode verfügbar.

any vs unknown im Überblick

anyunknown
Akzeptiert jeden Wertjaja
Einem string, einer number usw. zuweisenja, ungeprüftnein (TS2322)
Eine Eigenschaft lesen, eine Methode aufrufenja, ungeprüftnein (TS18046)
Als Funktion aufrufenjanein
Rechnen und vergleichen (x * 2, x + 1, x < 5)janein (TS18046)
Braucht eine Prüfung vor der Verwendungneinja (typeof, instanceof, in, ein Type Guard)
Wirkung auf die Typprüfungfür diesen Wert und alles, was er berührt, abgeschaltetbleibt an

Typtheoretisch ist unknown der Top Type: Jeder Typ ist ihm zuweisbar, und er selbst ist nur unknown und any zuweisbar. any ist ein Notausgang, der in beide Richtungen zuweisbar ist, zu allem außer never.

any schaltet die Typprüfung ab

Einem als any typisierten Wert wird blind vertraut. Der Compiler akzeptiert Tippfehler, falsche Typen und fehlende Eigenschaften, und die Fehler tauchen stattdessen zur Laufzeit auf.

Die Ausgabe zeigt das Problem: Eine als number annotierte Variable enthält einen String, und die letzte Zeile wirft Cannot read properties of undefined (reading 'city'). any breitet sich außerdem aus. user.name ist any, also ist jeder daraus berechnete Wert any, und ein einziger untypisierter Wert kann die Prüfung weit entfernt von der Stelle abschalten, an der er hereinkam.

unknown zwingt dich zuerst zu prüfen

Bei unknown verweigert der Compiler jede Operation, bis der Code den Wert einengt. Das Einengen nutzt gewöhnliche JavaScript-Prüfungen, und in jedem Zweig hat der Wert den geprüften Typ.

Die Prüfung value === null muss vor der Objektprüfung stehen, weil typeof null gleich "object" ist. Nach "id" in value weiß TypeScript, dass das Objekt eine Eigenschaft id hat, typisiert als unknown, da nichts sagt, was sie enthält. String() macht daraus ausdrücklich Text; um sie als Zahl zu verwenden, würdest du sie zuerst mit typeof prüfen.

Du kannst die Prüfung auch mit einer Assertion überspringen, value as string, und der Compiler akzeptiert das. Das ist ein Versprechen ohne Prüfung zur Laufzeit dahinter, bevorzuge also eine echte Prüfung; siehe Typ-Assertions.

JSON mit unknown validieren

JSON.parse ist so deklariert, dass es any zurückgibt, sein Ergebnis schaltet die Prüfung also stillschweigend ab. Annotiere das Ergebnis als unknown und schreibe einen Type Guard, der die Form prüft, bevor der restliche Code ihr vertraut.

Die erste Eingabe gibt dark at 14px aus, die zweite wird abgelehnt, weil fontSize fehlt. Mit any wäre die zweite Eingabe als Settings mit undefiniertem fontSize durchgelaufen. Bei großen Schemas erledigt eine Validierungsbibliothek dieselbe Aufgabe und leitet den Typ für dich ab.

noImplicitAny

any kommt nicht nur von Leuten, die es schreiben. Ein Parameter ohne Annotation und ohne Kontext, aus dem sich ableiten ließe, wäre ebenfalls any. Die Option noImplicitAny, die strict einschaltet, meldet das als Fehler:

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

Die Lösung ist eine Annotation, function double(x: number). Ausdrücklich x: any zu schreiben kompiliert auch, und genau darum geht es: any bleibt im Code sichtbar und lässt sich suchen und prüfen.

Wo sich any trotzdem einschleicht

Selbst unter strict erzeugen diese Quellen any, ohne dass das Wort in deinem Code steht:

QuelleWas du bekommstWas tun
JSON.parse(text)anyals unknown annotieren, dann validieren
response.json() mit den Browser-Typen (DOM)Promise<any>wie bei JSON.parse (die eigenen fetch-Typen von Node geben bereits Promise<unknown> zurück)
Ein Paket ohne Typdefinitionenany für seine Imports (mit Fehler, außer du ergänzt eine Deklaration)@types/... installieren oder eine .d.ts schreiben
value as anyanyeinen Type Guard oder eine genaue Assertion verwenden
catch (e) mit ausgeschaltetem useUnknownInCatchVariablesanystrict macht es zu unknown; lass es so

Die Variable in catch ist unter strict unknown, weil alles geworfen werden kann, nicht nur Error-Objekte. Enge sie mit e instanceof Error ein, bevor du e.message liest.

Wann any in Ordnung ist

any ist nicht verboten, aber jedes davon ist eine Stelle, die der Compiler nicht mehr schützt. Vernünftige Einsätze:

  • Die Migration einer JavaScript-Codebasis, in der any markiert, was noch nicht typisiert ist.
  • Code, den das Typsystem nicht gut ausdrücken kann, klein gehalten und hinter einer typisierten Funktionssignatur.
  • Testcode, der absichtlich fehlerhafte Daten übergibt.

Für alles andere deckt unknown denselben Fall „ich kenne diesen Typ nicht“ ab und behält die Prüfungen. Viele Teams setzen das mit der Lint-Regel @typescript-eslint/no-explicit-any durch. Ein verwandter Typ, Record<string, unknown>, ist die übliche Wahl für „irgendein Objekt mit unbekannten Werten“.

Häufig gestellte Fragen

Was ist der Unterschied zwischen any und unknown in TypeScript?

Beide akzeptieren jeden Wert. Mit any kannst du alles mit dem Wert tun (Eigenschaften lesen, ihn aufrufen, ihn einer number zuweisen), und der Compiler prüft nichts. Mit unknown kannst du fast nichts tun, bis du ihn mit einer Prüfung wie typeof x === "string" einengst. unknown lässt die Typprüfung an, deshalb ist es die sicherere Wahl.

Wann sollte ich unknown statt any verwenden?

Immer, wenn der Typ eines Werts beim Kompilieren nicht bekannt ist: geparstes JSON, Daten aus einer Netzwerkantwort, ein gefangener Fehler, die Eingabe einer Validierungsfunktion. Typisiere ihn als unknown und enge ihn ein. Greife zu any nur bei kurzfristiger Migrationsarbeit oder Code, den das Typsystem nicht beschreiben kann.

Was bedeutet "Object is of type 'unknown'"?

Die Fehler TS18046 ('x' is of type 'unknown') und TS2571 (Object is of type 'unknown', verwendet, wenn der Wert kein einfacher Name ist, etwa load().id) bedeuten, dass du einen unknown-Wert so verwendet hast, als hätte er einen bestimmten Typ, etwa durch das Lesen einer Eigenschaft oder den Aufruf einer Methode. Prüfe zuerst den Typ (typeof, instanceof, Array.isArray, in oder eine Type-Guard-Funktion) und verwende den Wert im eingeengten Zweig.

Warum gibt JSON.parse any zurück?

Seine Deklaration in der Standardbibliothek lautet parse(text: string, ...): any, weil der Compiler nicht wissen kann, was ein String enthält. Das Ergebnis schaltet stillschweigend die Prüfung für alles ab, was es berührt. Annotiere es stattdessen: const data: unknown = JSON.parse(text), und validiere es vor der Verwendung.

Was ist noImplicitAny?

Eine Compileroption, Teil von strict, die einen Fehler meldet, wenn eine Deklaration stillschweigend den Typ any bekäme, weil sie keine Annotation hat und nichts, woraus sich ableiten ließe: TS7006 bei einem Parameter, TS7005 oder TS7034 bei einer Variablen, deren Typ sich nicht ermitteln lässt. Sie verhindert, dass any auftaucht, ohne dass es jemand geschrieben hat. Ausdrückliches any bleibt erlaubt.

Coddy programming languages illustration

Lerne mit Coddy zu programmieren

LOS GEHT'S