Complex type manipulation including union/intersection handling, type extraction, generic transformations, and specialized type analysis.
Extract specific types from union types using type constraints.
/**
* Extract types from a union that match the given constraint
* Equivalent to TypeScript's Extract utility type
* @param v - Optional value for type inference
* @returns ExpectTypeOf for the extracted type (never if no match)
*/
extract<V>(v?: V): ExpectTypeOf<Extract<Actual, V>, Options>;Usage Examples:
import { expectTypeOf } from "expect-type";
// Responsive design type example
type ResponsiveProp<T> = T | T[] | { xs?: T; sm?: T; md?: T };
interface CSSProperties {
margin?: string;
padding?: string;
}
declare function getResponsiveProp<T>(props: T): ResponsiveProp<T>;
const cssProps: CSSProperties = { margin: "1px", padding: "2px" };
// Extract array type from union
expectTypeOf(getResponsiveProp(cssProps))
.extract<unknown[]>()
.toEqualTypeOf<CSSProperties[]>();
// Extract object type from union
expectTypeOf(getResponsiveProp(cssProps))
.extract<{ xs?: any }>()
.toEqualTypeOf<{
xs?: CSSProperties;
sm?: CSSProperties;
md?: CSSProperties;
}>();
// Extract never when no match
type UnionType = string | number | boolean;
expectTypeOf<UnionType>()
.extract<{ impossible: true }>()
.toBeNever();
// Extract specific union members
expectTypeOf<"a" | "b" | "c">()
.extract<"a" | "b">()
.toEqualTypeOf<"a" | "b">();Remove specific types from union types.
/**
* Remove types from a union that match the given constraint
* Equivalent to TypeScript's Exclude utility type
* @param v - Optional value for type inference
* @returns ExpectTypeOf for the remaining union type (never if all excluded)
*/
exclude<V>(v?: V): ExpectTypeOf<Exclude<Actual, V>, Options>;Usage Examples:
import { expectTypeOf } from "expect-type";
// Remove array and object types, keep primitive
type ResponsiveProp<T> = T | T[] | { xs?: T; sm?: T; md?: T };
declare function getResponsiveProp<T>(props: T): ResponsiveProp<T>;
const cssProps: CSSProperties = { margin: "1px" };
expectTypeOf(getResponsiveProp(cssProps))
.exclude<unknown[]>()
.exclude<{ xs?: unknown }>()
.toEqualTypeOf<CSSProperties>();
// Chain exclusions
expectTypeOf<string | number | boolean | null | undefined>()
.exclude<null>()
.exclude<undefined>()
.toEqualTypeOf<string | number | boolean>();
// Exclude multiple types at once
expectTypeOf<string | number | boolean | null | undefined>()
.exclude<null | undefined>()
.toEqualTypeOf<string | number | boolean>();
// Exclude all types results in never
expectTypeOf<"a" | "b">()
.exclude<"a" | "b">()
.toBeNever();
// Complex exclusion example
type ApiResponse<T> = T | { error: string } | null;
expectTypeOf<ApiResponse<{ data: number }>>()
.exclude<null>()
.exclude<{ error: string }>()
.toEqualTypeOf<{ data: number }>();Transform types through callback functions for complex type manipulation.
/**
* Transform type via a callback function
* The callback is not executed at runtime, only used for type inference
* @param fn - Transformation function for type inference
* @returns ExpectTypeOf for the transformed type
*/
map<T>(fn: (value: Actual) => T): ExpectTypeOf<T, Options>;Usage Examples:
import { expectTypeOf } from "expect-type";
// Transform string to capitalized version
const capitalize = <S extends string>(input: S) =>
(input.slice(0, 1).toUpperCase() + input.slice(1)) as Capitalize<S>;
expectTypeOf(capitalize)
.map(fn => fn("hello world"))
.toEqualTypeOf<"Hello world">();
// Transform with generic functions
declare function processData<T>(data: T[]): {
items: T[];
count: number;
};
expectTypeOf(processData)
.map(fn => fn([1, 2, 3]))
.toEqualTypeOf<{ items: number[]; count: number }>();
// Transform complex nested types
type AsyncData<T> = Promise<{ result: T; timestamp: number }>;
declare function fetchData<T>(id: string): AsyncData<T>;
expectTypeOf(fetchData)
.map(fn => fn("user-123"))
.resolves
.toEqualTypeOf<{ result: string; timestamp: number }>();Extract the resolved type from Promise-like types.
/**
* Extract the resolved value type from a Promise
* Only available when Actual extends PromiseLike<T>
*/
resolves: ExpectTypeOf<ResolvedType, Options>; // where Actual extends PromiseLike<ResolvedType>Usage Examples:
import { expectTypeOf } from "expect-type";
// Basic Promise resolution
expectTypeOf(Promise.resolve(42)).resolves.toBeNumber();
expectTypeOf(Promise.resolve("hello")).resolves.toBeString();
// Async function returns
async function fetchUser(): Promise<{ name: string; id: number }> {
return { name: "John", id: 1 };
}
expectTypeOf(fetchUser).returns.resolves.toEqualTypeOf<{
name: string;
id: number;
}>();
// Nested Promise resolution
const nestedPromise: Promise<Promise<string>> = Promise.resolve(
Promise.resolve("nested")
);
expectTypeOf(nestedPromise).resolves.resolves.toBeString();
// Promise-like objects (Thenable)
interface CustomThenable<T> {
then<R>(callback: (value: T) => R): CustomThenable<R>;
}
declare const customPromise: CustomThenable<number>;
expectTypeOf(customPromise).resolves.toBeNumber();
// Conditional Promise resolution
type MaybePromise<T> = T | Promise<T>;
declare function process<T>(input: MaybePromise<T>): T;
expectTypeOf<MaybePromise<string>>()
.extract<Promise<any>>()
.resolves.toBeString();Extract the item type from array-like structures.
/**
* Extract array item type
* Works with arrays, readonly arrays, and ArrayLike types
*/
items: ExpectTypeOf<ItemType, Options>; // where Actual extends ArrayLike<ItemType>Usage Examples:
import { expectTypeOf } from "expect-type";
// Basic array items
expectTypeOf([1, 2, 3]).items.toBeNumber();
expectTypeOf(["a", "b", "c"]).items.toBeString();
// Mixed type arrays
expectTypeOf([1, "two", true]).items.toEqualTypeOf<number | string | boolean>();
// Readonly arrays
expectTypeOf<readonly string[]>().items.toBeString();
// Tuple items (union of all types)
expectTypeOf<[string, number, boolean]>().items.toEqualTypeOf<string | number | boolean>();
// Nested array items
expectTypeOf([[1, 2], [3, 4]]).items.items.toBeNumber();
// ArrayLike structures
interface ArrayLike<T> {
readonly length: number;
readonly [n: number]: T;
}
declare const arrayLike: ArrayLike<string>;
expectTypeOf(arrayLike).items.toBeString();
// String as ArrayLike<string>
expectTypeOf("hello").items.toBeString();
// TypedArrays
expectTypeOf(new Int32Array([1, 2, 3])).items.toBeNumber();
expectTypeOf(new Uint8Array([1, 2, 3])).items.toBeNumber();Analyze type guard and assertion function behavior.
/**
* Extract the type guarded by a type guard function
* Works with functions returning `v is T`
*/
guards: ExpectTypeOf<GuardedType, Options>; // where function returns `v is GuardedType`
/**
* Extract the type asserted by an assertion function
* Works with functions using `asserts v is T`
*/
asserts: ExpectTypeOf<AssertedType, Options>; // where function uses `asserts v is AssertedType`Usage Examples:
import { expectTypeOf } from "expect-type";
// Basic type guards
function isString(value: unknown): value is string {
return typeof value === "string";
}
function isNumber(value: unknown): value is number {
return typeof value === "number";
}
expectTypeOf(isString).guards.toBeString();
expectTypeOf(isNumber).guards.toBeNumber();
// Complex type guards
interface User {
name: string;
age: number;
}
function isUser(value: unknown): value is User {
return (
typeof value === "object" &&
value !== null &&
typeof (value as any).name === "string" &&
typeof (value as any).age === "number"
);
}
expectTypeOf(isUser).guards.toEqualTypeOf<User>();
// Generic type guards
function isArray<T>(value: unknown, itemGuard: (item: unknown) => item is T): value is T[] {
return Array.isArray(value) && value.every(itemGuard);
}
expectTypeOf(isArray)
.map(fn => fn([], isString))
.guards.toEqualTypeOf<string[]>();
// Assertion functions
function assertIsString(value: unknown): asserts value is string {
if (typeof value !== "string") {
throw new TypeError("Expected string");
}
}
expectTypeOf(assertIsString).asserts.toBeString();
// Complex assertions
function assertIsUser(value: unknown): asserts value is User {
if (!isUser(value)) {
throw new TypeError("Expected User object");
}
}
expectTypeOf(assertIsUser).asserts.toEqualTypeOf<User>();Extract instance types from constructor functions and classes.
/**
* Extract the instance type of a constructor
* Only available when Actual is a constructor function
*/
instance: ExpectTypeOf<InstanceType<Actual>, Options>; // where Actual is constructorUsage Examples:
import { expectTypeOf } from "expect-type";
// Built-in constructors
expectTypeOf(Date).instance.toHaveProperty("toISOString");
expectTypeOf(Date).instance.toHaveProperty("getTime");
expectTypeOf(Array).instance.toHaveProperty("push");
expectTypeOf(Array).instance.toHaveProperty("length");
// Custom classes
class User {
constructor(public name: string, public age: number) {}
greet(): string {
return `Hello, I'm ${this.name}`;
}
}
expectTypeOf(User).instance.toHaveProperty("name").toBeString();
expectTypeOf(User).instance.toHaveProperty("age").toBeNumber();
expectTypeOf(User).instance.toHaveProperty("greet").returns.toBeString();
// Generic classes
class Container<T> {
constructor(public value: T) {}
getValue(): T {
return this.value;
}
}
declare const StringContainer: typeof Container<string>;
expectTypeOf(StringContainer).instance.toHaveProperty("value").toBeString();
expectTypeOf(StringContainer).instance.toHaveProperty("getValue").returns.toBeString();
// Abstract classes
abstract class Shape {
abstract area(): number;
describe(): string {
return `Area: ${this.area()}`;
}
}
class Circle extends Shape {
constructor(private radius: number) {
super();
}
area(): number {
return Math.PI * this.radius ** 2;
}
}
expectTypeOf(Circle).instance.toHaveProperty("area").returns.toBeNumber();
expectTypeOf(Circle).instance.toHaveProperty("describe").returns.toBeString();Combine multiple advanced type operations for sophisticated type testing.
import { expectTypeOf } from "expect-type";
// Complex API response handling
type ApiResponse<T> =
| { success: true; data: T }
| { success: false; error: string }
| null;
type UserData = { id: number; name: string };
// Extract successful response data
expectTypeOf<ApiResponse<UserData>>()
.exclude<null>()
.extract<{ success: true; data: any }>()
.toHaveProperty("data")
.toEqualTypeOf<UserData>();
// Complex generic transformation
type AsyncResult<T> = Promise<T | { error: string }>;
declare function fetchWithRetry<T>(
fetcher: () => Promise<T>
): AsyncResult<T>;
expectTypeOf(fetchWithRetry)
.map(fn => fn(() => Promise.resolve({ user: "test" })))
.resolves
.exclude<{ error: string }>()
.toEqualTypeOf<{ user: string }>();
// Nested container types
type Container<T> = {
items: T[];
metadata: {
count: number;
hasMore: boolean;
};
};
type AsyncContainer<T> = Promise<Container<T>>;
expectTypeOf<AsyncContainer<string>>()
.resolves
.toHaveProperty("items")
.items
.toBeString();
expectTypeOf<AsyncContainer<User>>()
.resolves
.toHaveProperty("metadata")
.toHaveProperty("count")
.toBeNumber();Performance: Complex type operations like .branded can cause TypeScript to slow down or give up on very deep types.
Type Depth: Very deeply nested type transformations may hit TypeScript's recursion limits.
Generic Resolution: Some advanced operations may not work perfectly with unresolved generic types.
Runtime Safety: These operations are purely compile-time - they provide no runtime type checking or validation.