This TypeScript error occurs when trying to access properties or methods on a value typed as `unknown` without first narrowing its type. It's most common in `try/catch` blocks where caught errors are `unknown` by default. The fix involves using type guards like `instanceof`, `typeof`, or type assertions to safely work with unknown values.
The "Object is of type 'unknown'" error is TypeScript's way of enforcing type safety when working with values that could be anything. Unlike the `any` type which allows any operation, `unknown` is a type-safe top type that requires explicit type checking before use. This error typically appears in scenarios like: 1. Working with errors in `catch` blocks (where TypeScript 4.4+ makes caught errors `unknown` by default) 2. Parsing JSON data with `JSON.parse()` 3. Working with values from external APIs or user input 4. Using generic functions that return `unknown` The `unknown` type was introduced in TypeScript 3.0 as a type-safe alternative to `any`. It represents "any possible value" but forces you to verify what you're working with before using it, preventing runtime errors that can occur with `any`.
For errors and other class instances, use instanceof to narrow the type:
try {
// Some code that might throw
} catch (error) {
// TypeScript error: Object is of type 'unknown'
// console.log(error.message); // Error!
if (error instanceof Error) {
// Now TypeScript knows error is an Error
console.log(error.message); // Works!
}
}This is the safest approach for Error objects and other class instances.
For primitive values (string, number, boolean, etc.), use typeof checks:
function processValue(value: unknown) {
// TypeScript error: Object is of type 'unknown'
// value.toUpperCase(); // Error!
if (typeof value === 'string') {
// Now TypeScript knows value is a string
console.log(value.toUpperCase()); // Works!
}
}Common typeof checks: 'string', 'number', 'boolean', 'object', 'function', 'undefined'.
For objects with specific shapes, create reusable type guard functions:
interface ApiResponse {
data: unknown;
status: number;
}
function isApiResponse(obj: unknown): obj is ApiResponse {
return (
typeof obj === 'object' &&
obj !== null &&
'data' in obj &&
'status' in obj &&
typeof (obj as any).status === 'number'
);
}
async function fetchData() {
const response = await fetch('/api/data');
const data: unknown = await response.json();
if (isApiResponse(data)) {
// TypeScript now knows data is ApiResponse
console.log(`Status: ${data.status}`);
}
}Type predicates (obj is ApiResponse) tell TypeScript what type the value is after the check.
If you're absolutely sure about the type, use type assertions (cast with as):
try {
// Some code
} catch (error) {
// Assert error is an Error
const err = error as Error;
console.log(err.message);
// Or inline assertion
console.log((error as Error).message);
}Warning: Type assertions bypass TypeScript's type checking. Only use when you're certain of the type, as wrong assertions cause runtime errors.
If working with legacy code, you can configure TypeScript to use any instead of unknown for catch variables:
// tsconfig.json
{
"compilerOptions": {
"useUnknownInCatchVariables": false
}
}This reverts to pre-TypeScript 4.4 behavior where catch variables are any. Not recommended for new code as it reduces type safety.
Use unknown in function parameters when accepting any value but wanting type safety:
// Safe: Forces callers to handle unknown properly
function safeParse(json: string): unknown {
return JSON.parse(json);
}
// Usage:
const data = safeParse('{"name": "John"}');
if (typeof data === 'object' && data !== null && 'name' in data) {
console.log((data as any).name);
}
// Compare with unsafe version:
function unsafeParse(json: string): any {
return JSON.parse(json);
}
// No type checking required, but runtime errors possible
const unsafeData = unsafeParse('not json');
console.log(unsafeData.name); // Runtime error!The unknown type is part of TypeScript's type system hierarchy as a top type (along with any). Unlike any, which is both a top and bottom type, unknown is only a top type - it can be assigned to anything, but nothing can be assigned to it without type checking.
Key differences between `unknown` and `any`:
1. any disables all type checking; unknown requires type checking
2. any values can be assigned to any type; unknown values cannot without assertion
3. any allows property/method access; unknown does not
4. any is contagious (makes everything else any); unknown is not
When to use `unknown` vs `any`:
- Use unknown when you need maximum type safety and are willing to do type checking
- Use any only when dealing with truly dynamic code or migrating JavaScript
- Prefer unknown over any for new code
Performance considerations: Type checking with unknown happens at compile time, so there's no runtime overhead. The type guards and assertions are erased during compilation to JavaScript.
Function expression requires a return type
Function expression requires a return type
Value of type 'string | undefined' is not iterable
How to fix "Value is not iterable" in TypeScript
Type 'undefined' is not assignable to type 'string'
How to fix "Type undefined is not assignable to type string" in TypeScript
Type narrowing from typeof check produces 'never'
How to fix "Type narrowing produces never" in TypeScript
Type parameter 'T' has conflicting constraints
How to fix "Type parameter has conflicting constraints" in TypeScript