Skip to Content
๐Ÿ“– Guide DocumentsRuntime Validatorsassert() function

typia.assert() โ€” throw on the first invalid field

signature
function assert<T>(input: T): T; function assert<T>(input: unknown): T; function assert<T>(input: unknown, errorFactory?: (props: TypeGuardError.IProps) => Error): T;

typia.assert<T> is the throwing variant of is. If input matches T, it returns input unchanged. If it doesnโ€™t, it throws a TypeGuardError that points at exactly one failing property โ€” the first one it encounters.

Use it when you want an exception to propagate up the stack (HTTP request handlers, message queue consumers, boot-time config loading).

ScenarioPick
One field is wrong โ†’ throw an exceptionassert (you are here)
Just a yes/no answeris
Need every field error at once (form, LLM feedback loop)validate

First example

hello-assert.ts
import typia, { tags } from "typia"; interface User { id: string & tags.Format<"uuid">; age: number & tags.Type<"uint32"> & tags.Minimum<19>; } const user = typia.assert<User>(await loadUserFromRequest()); // โ†’ returns the same value, typed as User // โ†’ throws TypeGuardError if any field is wrong

If age is 18, you get a TypeGuardError carrying:

FieldValue
method"typia.assert"
path"$input.age"
expected"number & Minimum<19>"
value18

The error includes a default message ready for logging, but the structured fields above are usually what you want to surface to clients.

examples/src/validators/assert.ts
import typia, { tags } from "typia"; import { v4 } from "uuid"; typia.assert<IMember>({ id: v4(), email: "samchon.github@gmail.com", age: 18, // wrong, must be greater than 19 }); interface IMember { id: string & tags.Format<"uuid">; email: string & tags.Format<"email">; age: number & tags.Type<"uint32"> & tags.ExclusiveMinimum<19> & tags.Maximum<100>; }

TypeGuardError reference

class TypeGuardError<T = any> extends Error { readonly method: string; // "typia.assert", "typia.assertEquals", โ€ฆ readonly path: string | undefined; // e.g. "$input.user.age" readonly expected: string; // e.g. "string & Format<\"uuid\">" readonly value: unknown; // the offending value readonly description?: string; // hint when the value is `undefined` }

undefined

@samchon/typia
/** * Error thrown when type assertion fails. * * Thrown by {@link assert}, {@link assertGuard}, and other assert-family * functions when input doesn't match expected type `T`. Contains detailed * information about the first assertion failure: * * - `method`: Which typia function threw (e.g., `"typia.assert"`) * - `path`: Property path where error occurred (e.g., `"input.user.age"`) * - `expected`: Expected type string (e.g., `"number & ExclusiveMinimum<19>"`) * - `value`: Actual value that failed validation * * @template T Expected type (for type safety) */ export class TypeGuardError<T = any> extends Error { /** * Name of the typia method that threw this error. * * E.g., `"typia.assert"`, `"typia.assertEquals"`, `"typia.assertGuard"`. */ public readonly method: string; /** * Property path where assertion failed. * * Uses dot notation for nested properties. `undefined` if error occurred at * root level. * * E.g., `"input.age"`, `"input.profile.email"`, `"input[0].name"`. */ public readonly path: string | undefined; /** * String representation of expected type. * * E.g., `"string"`, `"number & ExclusiveMinimum<19>"`, `"{ name: string; age: * number }"`. */ public readonly expected: string; /** * Actual value that failed assertion. * * The raw value at the error path, useful for debugging. */ public readonly value: unknown; /** * Optional human-readable error description. * * Primarily for AI agent libraries or custom validation scenarios needing * additional context. Standard assertions rely on `path`, `expected`, and * `value` for error reporting. */ public readonly description?: string | undefined; /** * Phantom property for TypeScript type safety. * * Not used at runtimeโ€”exists only to preserve generic type `T` in the type * system. Always `undefined`. * * @internal */ protected readonly fake_expected_typed_value_?: T | undefined; /** * Creates a new TypeGuardError instance. * * @param props Error properties */ public constructor(props: TypeGuardError.IProps) { // MESSAGE CONSTRUCTION // Use custom message if provided, otherwise generate default format super( props.message || `Error on ${props.method}(): invalid type${ props.path ? ` on ${props.path}` : "" }, expect to be ${props.expected}`, ); // INHERITANCE POLYFILL // Set up prototype for compatibility across different JavaScript environments const proto = new.target.prototype; if (Object.setPrototypeOf) Object.setPrototypeOf(this, proto); else (this as any).__proto__ = proto; // ASSIGN MEMBERS this.name = "TypeGuardError"; this.method = props.method; this.path = props.path; this.expected = props.expected; this.value = props.value; if (props.description || props.value === undefined) this.description = props.description ?? [ "The value at this path is `undefined`.", "", `Please fill the \`${props.expected}\` typed value next time.`, ].join("\n"); } } export namespace TypeGuardError { /** Properties for constructing a TypeGuardError. */ export interface IProps { /** * Name of the typia method that threw the error. * * E.g., `"typia.assert"`, `"typia.assertEquals"`. */ method: string; /** * Property path where assertion failed (optional). * * E.g., `"input.age"`, `"input.profile.email"`. */ path?: undefined | string; /** * String representation of expected type. * * E.g., `"string"`, `"number & ExclusiveMinimum<19>"`. */ expected: string; /** Actual value that failed assertion. */ value: unknown; /** * Optional human-readable error description. * * For AI agent libraries or custom validation needing additional context. */ description?: string; /** * Custom error message (optional). * * If not provided, a default message is generated from other properties. */ message?: undefined | string; } }

The path uses $input.x.y notation so you can copy it into a UI error message without further processing.

Customizing the thrown error

Pass an errorFactory second argument to throw whatever your framework expects instead of TypeGuardError:

import typia from "typia"; class HttpException extends Error { constructor(message: string, public readonly status: number) { super(message); } } typia.assert<User>(input, (info) => new HttpException(`Invalid ${info.path}: expected ${info.expected}`, 400), );

The factory receives the same TypeGuardError.IProps and its return value is thrown directly.

Strict (assertEquals)

assert allows properties not declared on T. assertEquals rejects them โ€” the first extra key becomes the thrown error:

typia.assert<User>({ id: "...", age: 25, sex: 1 }); // ok (sex ignored) typia.assertEquals<User>({ id: "...", age: 25, sex: 1 }); // throws on `sex`

The thrown TypeGuardError for the extra property has expected: "undefined" and path pointing at the extra key:

FieldValue
method"typia.assertEquals"
path"$input.sex"
expected"undefined"
value1
examples/src/validators/assertEquals.ts
import typia, { tags } from "typia"; import { v4 } from "uuid"; typia.assertEquals<IMember>({ id: v4(), email: "samchon.github@gmail.com", age: 30, sex: 1, // extra }); interface IMember { id: string & tags.Format<"uuid">; email: string & tags.Format<"email">; age: number & tags.Type<"uint32"> & tags.ExclusiveMinimum<19> & tags.Maximum<100>; }

Assertion guards

typia.assert<T> returns the validated value:

const user: User = typia.assert<User>(input);

Thatโ€™s convenient but it makes you assign to a new variable. If youโ€™d rather narrow the original variable in place, use typia.assertGuard<T>. It returns nothing โ€” but its return type is asserts input is T, which is TypeScriptโ€™s assertion guard syntax: when a function with that signature returns without throwing, the compiler treats the argument as narrowed to T on every subsequent line. So you keep using the same variable instead of binding a new one:

import typia from "typia"; const input: unknown = await fetchSomething(); typia.assertGuard<User>(input); // from this line on, `input` is typed as User console.log(input.age);

typia.assertGuardEquals<T> is the strict-keys variant. Both throw TypeGuardError on mismatch.

examples/src/validators/assertGuard.ts
import typia from "typia"; interface IPoint { x: number; y: number; } const input: unknown = { x: 1, y: 2 }; // PERFORM THE ASSERTION GUARD typia.assertGuard<IPoint>(input); // FROM NOW ON, "input" IS THE "IPoint" TYPE input.x; // OK input.y; // OK

Reusable factories

Same idea as createIs โ€” when you call assert<T> from many places, hoist the checker once:

import typia, { AssertionGuard } from "typia"; export const assertUser = typia.createAssert<User>(); export const assertUserEquals = typia.createAssertEquals<User>(); export const guardUser: AssertionGuard<User> = typia.createAssertGuard<User>(); export const guardUserEquals: AssertionGuard<User> = typia.createAssertGuardEquals<User>();
Important

Annotate the variable type for createAssertGuard.

TypeScript cannot infer an asserts ... is T signature without an explicit annotation. If you write const guard = typia.createAssertGuard<User>(), the compiler will refuse to narrow input after guard(input). Always declare the variable as AssertionGuard<T>.

examples/src/validators/createAssertGuard.ts
import typia, { AssertionGuard } from "typia"; //MUST DECLARE THE VARIABLE TYPE const explicit: AssertionGuard<number> = typia.createAssertGuard<number>(); // IF NOT, COMPILATION ERROR BE OCCURRED const implicit = typia.createAssertGuard<number>();

createAssert* factories accept an optional errorFactory that applies to every call.

examples/src/validators/createAssert.ts
import typia, { tags } from "typia"; export const assertMember = typia.createAssert<IMember>(); interface IMember { id: string & tags.Format<"uuid">; email: string & tags.Format<"email">; age: number & tags.Type<"uint32"> & tags.ExclusiveMinimum<19> & tags.Maximum<100>; }

What it doesnโ€™t check

Same rules as is: user-defined classes are walked by property (no instanceof), and function-typed properties are skipped unless you turn on the functional transform flag in tsconfig.json.

Built-in classes (Date, Set<T>, Map<K, V>, Uint8Array) are checked end-to-end, including their elements:

examples/src/validators/is-map.ts
import typia from "typia"; typia.createIs<Map<string, boolean | number | string>>();

Constraints

Intersect type tags into the type to express formats, ranges, and length limits:

examples/src/validators/assert-custom-tags.ts
import typia, { tags } from "typia"; export const assertSomething = typia.createAssert<Something>(); //---- // DEFINE CUSTOM TYPE TAGS //---- type Dollar = tags.TagBase<{ kind: "dollar"; target: "string"; value: undefined; validate: `$input[0] === "$" && !isNaN(Number($input.substring(1).split(",").join("")))`; }>; type Postfix<Value extends string> = tags.TagBase<{ kind: "postfix"; target: "string"; value: Value; validate: `$input.endsWith("${Value}")`; }>; type IsEven<Value extends number | bigint> = tags.TagBase<{ kind: "isEven"; target: Value extends number ? "number" : "bigint"; value: undefined; validate: `$input % ${Numeric<2>} === ${Numeric<0>}`; }>; type Numeric<Value extends number | bigint> = Value extends number ? Value : `BigInt(${Value})`; //---- // VALIDATION //---- interface Something { dollar: string & Dollar; postfix: string & Postfix<"!!!">; isEven: number & IsEven<number>; }

Performance

Same emission strategy as is, plus the per-field error-throwing branches. Still up to 20,000ร— faster than class-validator on hard-to-validate union structures, and still the only listed library that handles arbitrary unions. The structural feature matrix (recursive unions, template literals, rest tuples, etc.) lives on is ยง Performance โ€” same coverage, since assert is is plus error paths.

Assert Function Benchmark

Measured on AMD Ryzen 9 7940HS, Rog Flow x13ย 

Where to go next

  • Just want a boolean โ†’ is
  • Want every error at once โ†’ validate
  • Constraints (formats, ranges, lengths) โ†’ Special Tags
Last updated on