ZetCode

TypeScript unknown Type

last modified October 15, 2026

The unknown type represents a value whose type is not known yet. Like any, it accepts any value. Unlike any, it does not allow anything to be done with the value until its type is checked. This makes unknown the type-safe choice for data coming from outside the program, such as user input, JSON, or caught errors.

Assigning to unknown

Any value can be assigned to a variable of type unknown.

assign.ts
let value: unknown;

value = 42;
console.log(value);

value = "falcon";
console.log(value);

value = [1, 2, 3];
console.log(value);

value = { name: "Alice" };
console.log(value);

The variable holds a number, a string, an array, and an object in turn. Passing an unknown value to console.log is allowed, because console.log accepts any value.

$ npx tsx assign.ts
42
falcon
[ 1, 2, 3 ]
{ name: 'Alice' }

Using an unknown value

Properties and methods of an unknown value cannot be accessed, and the value cannot be assigned to a more specific type.

use_unknown.ts
const value: unknown = "falcon";

console.log(value.toUpperCase());
const text: string = value;

The compiler rejects both lines. With any, the same code would compile, and a wrong assumption about the value would only show up at runtime.

$ npx tsc --noEmit
use_unknown.ts(3,13): error TS18046: 'value' is of type 'unknown'.
use_unknown.ts(4,7): error TS2322: Type 'unknown' is not assignable to type 'string'.

Narrowing with typeof

Before an unknown value is used, its type must be narrowed. The typeof operator narrows primitive types.

narrow_typeof.ts
function describe(value: unknown): string {
    if (typeof value === "string") {
        return `string of length ${value.length}`;
    }

    if (typeof value === "number") {
        return `number ${value.toFixed(2)}`;
    }

    if (typeof value === "boolean") {
        return value ? "true value" : "false value";
    }

    return "something else";
}

console.log(describe("falcon"));
console.log(describe(3.14159));
console.log(describe(false));
console.log(describe(null));

Inside each if block, TypeScript knows the exact type of value, so length and toFixed can be used.

$ npx tsx narrow_typeof.ts
string of length 6
number 3.14
false value
something else

Narrowing with instanceof

Objects created by classes are narrowed with the instanceof operator.

narrow_instanceof.ts
function format(value: unknown): string {
    if (value instanceof Date) {
        return value.toISOString().slice(0, 10);
    }

    if (Array.isArray(value)) {
        return `array with ${value.length} elements`;
    }

    return String(value);
}

console.log(format(new Date(2026, 9, 15, 12)));
console.log(format(["a", "b", "c"]));
console.log(format(100));

The instanceof Date check narrows the value to Date. The Array.isArray function narrows it to an array. The String function converts any value to a string.

$ npx tsx narrow_instanceof.ts
2026-10-15
array with 3 elements
100

Checking object properties

To work with objects, we check that the value is a non-null object and that it has the expected properties. The in operator narrows the type to an object with the given property.

narrow_object.ts
function getName(value: unknown): string | undefined {
    if (typeof value === "object" && value !== null && "name" in value) {
        if (typeof value.name === "string") {
            return value.name;
        }
    }

    return undefined;
}

console.log(getName({ name: "Alice", age: 30 }));
console.log(getName({ name: 42 }));
console.log(getName("Alice"));

The null check is needed because typeof null is "object". After the in check, value.name has the type unknown, so its type is checked as well.

$ npx tsx narrow_object.ts
Alice
undefined
undefined

Custom type guards

Repeated checks are best moved into a type guard function. Its return type value is User tells the compiler what a true result means.

type_guard.ts
type User = {
    name: string;
    age: number;
};

function isUser(value: unknown): value is User {
    return (
        typeof value === "object" &&
        value !== null &&
        "name" in value &&
        "age" in value &&
        typeof value.name === "string" &&
        typeof value.age === "number"
    );
}

const data: unknown[] = [
    { name: "Alice", age: 30 },
    { name: "Bob" },
    "Carol",
];

for (const item of data) {
    if (isUser(item)) {
        console.log(`${item.name} is ${item.age} years old`);
    } else {
        console.log("not a user:", item);
    }
}

In the if branch, item has the User type. The type guard is only as reliable as the checks inside it; the compiler does not verify that they match the User type.

$ npx tsx type_guard.ts
Alice is 30 years old
not a user: { name: 'Bob' }
not a user: Carol

Parsing JSON

The JSON.parse method returns any. Assigning its result to unknown forces the data to be validated before use.

parse_json.ts
type Product = {
    id: number;
    title: string;
};

function isProduct(value: unknown): value is Product {
    return (
        typeof value === "object" &&
        value !== null &&
        "id" in value &&
        "title" in value &&
        typeof value.id === "number" &&
        typeof value.title === "string"
    );
}

function parseProduct(json: string): Product {
    const data: unknown = JSON.parse(json);

    if (!isProduct(data)) {
        throw new Error("Invalid product data");
    }

    return data;
}

const product = parseProduct('{"id": 1, "title": "Keyboard"}');
console.log(product.title);

try {
    parseProduct('{"id": "1", "name": "Mouse"}');
} catch (e) {
    console.log(e instanceof Error ? e.message : e);
}

The second JSON string has a string id and no title, so it is rejected. For larger data structures, validation libraries such as Zod generate such checks from a schema.

$ npx tsx parse_json.ts
Keyboard
Invalid product data

Errors in catch blocks

In JavaScript, any value can be thrown, not only Error objects. With the strict option, which includes useUnknownInCatchVariables, the variable of a catch clause has the unknown type.

catch_error.ts
function risky(kind: number): void {
    if (kind === 1) {
        throw new RangeError("value out of range");
    }

    if (kind === 2) {
        throw "a plain string";
    }
}

for (const kind of [1, 2]) {
    try {
        risky(kind);
    } catch (e) {
        if (e instanceof Error) {
            console.log(`${e.name}: ${e.message}`);
        } else {
            console.log("unknown error:", e);
        }
    }
}

The instanceof Error check makes the name and message properties available. The second call throws a string, which is handled by the else branch.

$ npx tsx catch_error.ts
RangeError: value out of range
unknown error: a plain string

Type assertions

A type assertion converts unknown to another type without any check. It should only be used when the type is guaranteed by other means.

assertion.ts
const input: unknown = "hello there";

const text = input as string;
console.log(text.toUpperCase());

const wrong = 42 as unknown as string;
console.log(typeof wrong);

The second assertion compiles, although the value is a number. The compiler trusts the assertion, and the program would fail as soon as a string method was called on wrong. Narrowing is the safer alternative.

$ npx tsx assertion.ts
HELLO THERE
number

unknown in union and intersection types

Because every type is assignable to unknown, it absorbs other types in a union. In an intersection, it disappears.

union_unknown.ts
type A = unknown | string;
type B = unknown & string;

const a: A = 100;
const b: B = "text";

console.log(typeof a, typeof b);

The type A is just unknown, so a number can be assigned to it. The type B is string.

$ npx tsx union_unknown.ts
number string

unknown versus any

Both types accept every value. The difference is in what the compiler allows afterwards.

Best Practices

Source

TypeScript Handbook: unknown

This tutorial covered the TypeScript unknown type. Use it for values of unknown origin and narrow them before use.

Author

My name is Jan Bodnar, and I am a passionate programmer with extensive programming experience. I have been writing programming articles since 2007. To date, I have authored over 1,400 articles and 8 e-books. I possess more than ten years of experience in teaching programming.

List all TypeScript tutorials.