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>.

AssertionGuard<T> is one parameter wide, so it also hides the per-call errorFactory described below. Annotate with (input: unknown, errorFactory?: (props: TypeGuardError.IProps) => Error) => asserts input is T when you need that override.

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. The function they return takes that same factory as an optional second parameter, which overrides the factory for that one call:

const assertUser = typia.createAssert<User>( (info) => new HttpException(`Invalid ${info.path}`, 400), ); assertUser(input); // throws HttpException assertUser(input, (info) => new Error(`audit: ${info.path}`)); // throws Error
Warning

Do not hand a created function straight to .map().

Array#map, forEach, and filter pass the element index as their second argument, which lands in errorFactory. TypeScript rejects rows.map(assertUser) for that reason; write rows.map((row) => assertUser(row)) instead.

Code compiled against typia 13.2.0 or earlier still type-checks the direct form, because those releases declared the returned function with one parameter. Run it on this release and the index is ignored: the failing element raises a TypeGuardError instead of the configured error. Run it on 13.2.0 and it raises TypeError: factory is not a function for every element but the first.

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