or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

advanced-types.mdbasic-types.mdfunctions.mdindex.mdobjects.mdtype-equality.md
tile.json

advanced-types.mddocs/

Advanced Type Operations

Complex type manipulation including union/intersection handling, type extraction, generic transformations, and specialized type analysis.

Capabilities

Union Type Extraction

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">();

Union Type Exclusion

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 }>();

Type Transformation

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 }>();

Promise Resolution

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();

Array Item Types

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();

Type Guard Analysis

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>();

Instance Type Analysis

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 constructor

Usage 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();

Complex Type Combinations

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();

Limitations

  1. Performance: Complex type operations like .branded can cause TypeScript to slow down or give up on very deep types.

  2. Type Depth: Very deeply nested type transformations may hit TypeScript's recursion limits.

  3. Generic Resolution: Some advanced operations may not work perfectly with unresolved generic types.

  4. Runtime Safety: These operations are purely compile-time - they provide no runtime type checking or validation.