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
any | unknown | |
|---|---|---|
| Akzeptiert jeden Wert | ja | ja |
Einem string, einer number usw. zuweisen | ja, ungeprüft | nein (TS2322) |
| Eine Eigenschaft lesen, eine Methode aufrufen | ja, ungeprüft | nein (TS18046) |
| Als Funktion aufrufen | ja | nein |
Rechnen und vergleichen (x * 2, x + 1, x < 5) | ja | nein (TS18046) |
| Braucht eine Prüfung vor der Verwendung | nein | ja (typeof, instanceof, in, ein Type Guard) |
| Wirkung auf die Typprüfung | für diesen Wert und alles, was er berührt, abgeschaltet | bleibt 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:
| Quelle | Was du bekommst | Was tun |
|---|---|---|
JSON.parse(text) | any | als 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 Typdefinitionen | any für seine Imports (mit Fehler, außer du ergänzt eine Deklaration) | @types/... installieren oder eine .d.ts schreiben |
value as any | any | einen Type Guard oder eine genaue Assertion verwenden |
catch (e) mit ausgeschaltetem useUnknownInCatchVariables | any | strict 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
anymarkiert, 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.