TS2571: Object is of type 'unknown'

Quick answer

  • You used an unknown value without proving what it is. TypeScript blocks every operation on unknown until you narrow it.
  • Caught error? (the usual cause) — if (e instanceof Error) e.message.
  • Arbitrary value? — guard with typeof, in, or a custom type-predicate function.
  • External JSON? — validate it into a real type (a type guard or a schema like Zod), don't blind-cast.
  • The bare-identifier form is the near-identical TS18046: 'x' is of type 'unknown' — same cause, same fixes.

The exact error string

try {
  doSomething();
} catch (e) {
  console.log(e.message);
  //          ~~~~~~~~~
  // error TS2571: Object is of type 'unknown'.
}

For a plain named variable, recent TypeScript versions print the sibling wording TS18046: 'e' is of type 'unknown'. It's the same situation — e is unknown and you tried to read .message off it before establishing that it has one. Everything on this page fixes both codes.

unknown is TypeScript's safe top type. Any value is assignable to an unknown, but you can do almost nothing with an unknown — no property access, no call, no arithmetic — until you narrow it to something more specific. TS2571 is the compiler refusing to let you touch a value whose shape it can't guarantee. That's the opposite of any, which lets you do anything and catches nothing.

Where did the unknown come from? (triage)

Every fix starts with identifying the source. There are three common ones:

Match your case to the matching fix below.

Fix 1: narrow a caught error before using it

Because JavaScript can throw any value — a string, a number, an object, not just an Error — TypeScript can't assume a caught value has .message or .stack. Narrow it with instanceof:

try {
  doSomething();
} catch (e) {
  if (e instanceof Error) {
    console.log(e.message);      // ✅ e is Error here
  } else {
    console.log(String(e));      // ✅ handle non-Error throws
  }
}

For a one-liner where you just want a message string, a conditional expression is enough: const msg = e instanceof Error ? e.message : String(e);. If you catch a lot of errors, factor the narrowing into a helper:

function toError(e: unknown): Error {
  return e instanceof Error ? e : new Error(String(e));
}

// usage
catch (e) { logger.error(toError(e).message); }

Fix 2: type-guard an arbitrary unknown value

When the value isn't an Error but some data whose shape you know at runtime, use the standard narrowing operators — typeof for primitives, Array.isArray, and the in operator (or a property check) for object shape:

function len(value: unknown): number {
  if (typeof value === "string") return value.length;   // ✅ string here
  if (Array.isArray(value)) return value.length;        // ✅ unknown[] here
  return 0;
}

function getId(value: unknown): string | null {
  // narrow to an object that has a string 'id'
  if (typeof value === "object" && value !== null && "id" in value
      && typeof (value as { id: unknown }).id === "string") {
    return (value as { id: string }).id;                // ✅ checked at runtime
  }
  return null;
}

For anything you reuse, wrap the check in a type predicate so the narrowing is named and reusable — the value is User return type teaches the compiler what a true result means:

interface User { id: string; name: string; }

function isUser(v: unknown): v is User {
  return typeof v === "object" && v !== null
      && typeof (v as any).id === "string"
      && typeof (v as any).name === "string";
}

if (isUser(data)) {
  console.log(data.name);   // ✅ data is User inside this block
}

Fix 3: validate external / JSON data into a real type

The reason to type parsed JSON as unknown (rather than any) is precisely so TS2571 forces you to validate it. Note that JSON.parse itself returns any, so the unknown usually comes from your own annotation or a typed fetch wrapper — and that annotation is a good habit, because data from the network is not guaranteed to match your interface. Rather than writing guards by hand for large shapes, define the target type once and validate against it:

// annotate the parsed result as unknown to force a check
const data: unknown = JSON.parse(rawBody);

// hand-rolled assertion function (throws if the shape is wrong)
function assertUser(v: unknown): asserts v is User {
  if (!isUser(v)) throw new Error("response is not a User");
}

assertUser(data);
console.log(data.name);   // ✅ data is User from here on

For real payloads a schema validator such as Zod is less error-prone than a hand-written guard, because one schema gives you both the runtime check and the static type (z.infer). Whichever you choose, start from an accurate interface: paste a sample response into JSON to TypeScript to generate the User type, then write your guard or schema against it. That keeps the shape you validate identical to the shape the rest of your code relies on — the same discipline that prevents a downstream TS2532 "Object is possibly 'undefined'" when an optional field is missing.

Fix 4: assert the type only when you're certain

If you genuinely know the type — for example the value came from your own code a line earlier — a specific assertion is acceptable and still far better than any, because every use after the assertion is fully type-checked:

const user = data as User;   // asserts the type; no runtime check happens
console.log(user.name);      // ✅ checked against User from here

The trade-off is honest: an assertion is a promise you make to the compiler and is not verified at runtime, so if the value isn't really a User, the bug surfaces later as undefined access. Use an assertion for values you control; use validation (Fix 3) for values you don't.

What NOT to do

Two tempting shortcuts undo the safety the unknown was giving you:

Debugging checklist

Frequently Asked Questions

What does TS2571: Object is of type 'unknown' mean?

You tried to use a value whose type is unknown — read a property, call it, or index into it — without first proving what it is. unknown is TypeScript's safe top type: any value is assignable to it, but you can do almost nothing with it until you narrow it. The fix is to narrow the value with a type guard, validation, or an assertion before you use it.

Why is my caught error of type unknown?

Since TypeScript 4.4, catch-clause variables are typed unknown by default under strict mode (useUnknownInCatchVariables). This is deliberate — anything can be thrown in JavaScript, not just Error objects. Narrow it before use: if (e instanceof Error) { console.log(e.message) } else { console.log(String(e)) }.

What is the difference between TS2571 and TS18046?

They are the same root cause with slightly different wording. TS2571 'Object is of type unknown' fires when you use an unknown-typed expression; TS18046 "'x' is of type unknown" fires for a bare named identifier in recent TypeScript versions. Both mean the value is unknown and both are fixed the same way — narrow it before using it.

How do I fix 'Object is of type unknown' for JSON or API data?

Validate the data into a known type instead of asserting it blindly. Write a type-guard function or use a schema validator (like Zod) that checks the shape at runtime and returns a typed value. Generate the target interface first with a tool like JSON to TypeScript, then validate the parsed data against it so the rest of your code is fully typed.

Should I just cast unknown to any to fix TS2571?

No. Casting to any disables all checking for that value, which defeats the reason it was unknown in the first place — TypeScript can no longer catch a wrong property or method on it. Prefer a type guard or validation. If you are genuinely certain of the type, a specific assertion (value as User) is far safer than any because it still type-checks every use afterward.

Why is unknown safer than any?

any switches type-checking off — you can do anything to an any value and TypeScript stays silent, so bugs slip through to runtime. unknown keeps checking on: you can hold any value in it, but you must narrow it before doing anything, so the compiler forces you to handle the actual shape. TS2571 is that safety net doing its job.

Type your JSON, skip the guards

Generate an accurate interface from a sample response, then validate against it — and browse the full TypeScript error reference.

All Error References JSON to TypeScript TS2532: possibly undefined
About the author

Pasindu Ishan is a software developer based in Sri Lanka. He builds privacy-first developer tools at JSON Dev Tools.