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

objects.mddocs/

Object and Property Testing

Object structure validation, property checking, and type manipulation utilities for testing object types and their properties.

Capabilities

Property Existence Checking

Verify whether objects have specific properties and access their types.

/**
 * Check if object has a given property and return its type for further testing
 * @param key - Property key to check for
 * @returns ExpectTypeOf for the property type if it exists, or true if using .not
 */
toHaveProperty<KeyType extends keyof Actual>(
  key: KeyType
): KeyType extends keyof Actual ? PositiveExpectTypeOf<Actual[KeyType]> : true;

Usage Examples:

import { expectTypeOf } from "expect-type";

const user = {
  name: "John",
  age: 30,
  email: "john@example.com",
  isActive: true
};

// Check property existence
expectTypeOf(user).toHaveProperty("name");
expectTypeOf(user).toHaveProperty("age");
expectTypeOf(user).not.toHaveProperty("address");

// Chain property type checking
expectTypeOf(user).toHaveProperty("name").toBeString();
expectTypeOf(user).toHaveProperty("age").toBeNumber();
expectTypeOf(user).toHaveProperty("isActive").toBeBoolean();

// Nested object properties
const nestedObj = {
  data: {
    items: [1, 2, 3],
    metadata: { count: 3 }
  }
};

expectTypeOf(nestedObj).toHaveProperty("data");
expectTypeOf(nestedObj.data).toHaveProperty("items");
expectTypeOf(nestedObj.data).toHaveProperty("metadata");
expectTypeOf(nestedObj.data.items).toBeArray();

Property Selection (Pick)

Select a subset of properties from an object type.

/**
 * Select specific properties from an object type
 * Equivalent to TypeScript's Pick utility type
 * @param keyToPick - Property key to pick (optional for type inference)
 * @returns ExpectTypeOf for the picked property subset
 */
pick<KeyToPick extends keyof Actual>(
  keyToPick?: KeyToPick
): ExpectTypeOf<Pick<Actual, KeyToPick>, Options>;

Usage Examples:

import { expectTypeOf } from "expect-type";

interface Person {
  name: string;
  age: number;
  email: string;
  address: {
    street: string;
    city: string;
  };
}

// Pick single property
expectTypeOf<Person>()
  .pick<"name">()
  .toEqualTypeOf<{ name: string }>();

// Pick multiple properties
expectTypeOf<Person>()
  .pick<"name" | "age">()
  .toEqualTypeOf<{ name: string; age: number }>();

// Chain with other operations
expectTypeOf<Person>()
  .pick<"address">()
  .toHaveProperty("address")
  .toEqualTypeOf<{ street: string; city: string }>();

// Pick with computed types
type UserKeys = "name" | "email";
expectTypeOf<Person>()
  .pick<UserKeys>()
  .toEqualTypeOf<{ name: string; email: string }>();

Property Omission

Remove specific properties from an object type.

/**
 * Remove specific properties from an object type
 * Equivalent to TypeScript's Omit utility type
 * @param keyToOmit - Property key to omit (optional for type inference)
 * @returns ExpectTypeOf for the object type without the omitted properties
 */
omit<KeyToOmit extends keyof Actual | (PropertyKey & Record<never, never>)>(
  keyToOmit?: KeyToOmit
): ExpectTypeOf<Omit<Actual, KeyToOmit>, Options>;

Usage Examples:

import { expectTypeOf } from "expect-type";

interface Person {
  name: string;
  age: number;
  email: string;
  ssn: string; // sensitive data
}

// Omit single property
expectTypeOf<Person>()
  .omit<"ssn">()
  .toEqualTypeOf<{ name: string; age: number; email: string }>();

// Omit multiple properties
expectTypeOf<Person>()
  .omit<"ssn" | "age">()
  .toEqualTypeOf<{ name: string; email: string }>();

// Create public user type
type PublicUser = Omit<Person, "ssn">;
expectTypeOf<Person>()
  .omit<"ssn">()
  .toEqualTypeOf<PublicUser>();

// Omit with union types
expectTypeOf<Person>()
  .omit<"ssn" | "email">()
  .not.toHaveProperty("ssn");

Complex Object Matching

Advanced object structure validation with deep property checking.

Usage Examples:

import { expectTypeOf } from "expect-type";

// Deep object structure
const complexObject = {
  user: {
    profile: {
      name: "Alice",
      age: 25,
      preferences: {
        theme: "dark" as const,
        notifications: true
      }
    },
    permissions: ["read", "write"] as const
  },
  metadata: {
    created: new Date(),
    version: "1.0.0"
  }
};

// Check deep property existence
expectTypeOf(complexObject)
  .toHaveProperty("user")
  .toHaveProperty("profile")
  .toHaveProperty("preferences")
  .toHaveProperty("theme")
  .toEqualTypeOf<"dark">();

// Verify nested array properties
expectTypeOf(complexObject)
  .toHaveProperty("user")
  .toHaveProperty("permissions")
  .items.toEqualTypeOf<"read" | "write">();

// Partial object matching
expectTypeOf(complexObject).toMatchObjectType<{
  user: {
    profile: {
      name: string;
    };
  };
  metadata: {
    version: string;
  };
}>();

Optional and Required Properties

Handle optional properties and distinguish them from required ones.

import { expectTypeOf } from "expect-type";

interface ConfigOptions {
  apiUrl: string;        // required
  timeout?: number;      // optional
  retries?: number;      // optional
  debug: boolean;        // required
}

// Required properties must exist
expectTypeOf<ConfigOptions>().toHaveProperty("apiUrl");
expectTypeOf<ConfigOptions>().toHaveProperty("debug");

// Optional properties can be checked
expectTypeOf<ConfigOptions>().toHaveProperty("timeout");
expectTypeOf<ConfigOptions>().toHaveProperty("retries");

// Distinguish between optional and required
expectTypeOf<{ a?: number }>().not.toEqualTypeOf<{}>();
expectTypeOf<{ a?: number }>().not.toEqualTypeOf<{ a: number }>();
expectTypeOf<{ a?: number }>().not.toEqualTypeOf<{ a: number | undefined }>();

// Optional vs undefined union
expectTypeOf<{ a?: number | null }>()
  .not.toEqualTypeOf<{ a: number | null }>();

// Nested optional properties
expectTypeOf<{ a: { b?: number } }>()
  .not.toEqualTypeOf<{ a: {} }>();

Readonly Properties

Distinguish between regular and readonly properties.

import { expectTypeOf } from "expect-type";

type MutableData = {
  name: string;
  count: number;
};

type ImmutableData = {
  readonly name: string;
  readonly count: number;
};

type MixedData = {
  name: string;
  readonly id: number;
};

// Readonly extends mutable (covariance)
expectTypeOf<ImmutableData>().toExtend<MutableData>();

// But they're not equal
expectTypeOf<ImmutableData>().not.toEqualTypeOf<MutableData>();

// Deep readonly differences
type DeepMutable = {
  user: { name: string; age: number };
};

type DeepReadonly = {
  user: { readonly name: string; readonly age: number };
};

expectTypeOf<DeepReadonly>().toExtend<DeepMutable>();
expectTypeOf<DeepReadonly>().not.toEqualTypeOf<DeepMutable>();

// Mixed readonly properties
expectTypeOf<MixedData>()
  .pick<"name">()
  .toEqualTypeOf<{ name: string }>();

expectTypeOf<MixedData>()
  .pick<"id">()
  .toEqualTypeOf<{ readonly id: number }>();

Index Signatures

Handle objects with index signatures and dynamic properties.

import { expectTypeOf } from "expect-type";

// String index signature
interface StringIndexed {
  [key: string]: number;
}

// Number index signature  
interface NumberIndexed {
  [index: number]: string;
}

// Mixed known and index properties
interface MixedIndex {
  name: string;
  [key: string]: string | number;
}

// Check index signature behavior
expectTypeOf<StringIndexed>().toHaveProperty("anyKey");
expectTypeOf<NumberIndexed>().toHaveProperty(0);
expectTypeOf<NumberIndexed>().toHaveProperty(999);

// Mixed index validation
const mixedObj: MixedIndex = { name: "test", age: 30, active: 1 };
expectTypeOf(mixedObj).toHaveProperty("name").toBeString();
expectTypeOf(mixedObj).toHaveProperty("age"); // exists via index signature

Class Instances

Validate class instance types and their properties.

import { expectTypeOf } from "expect-type";

class User {
  constructor(
    public name: string,
    private _id: number,
    protected role: string = "user"
  ) {}

  getName(): string {
    return this.name;
  }

  private getId(): number {
    return this._id;
  }
}

// Public properties and methods
expectTypeOf<User>().toHaveProperty("name");
expectTypeOf<User>().toHaveProperty("getName");

// Private/protected members not accessible
expectTypeOf<User>().not.toHaveProperty("_id");
expectTypeOf<User>().not.toHaveProperty("getId");
expectTypeOf<User>().not.toHaveProperty("role");

// Method return types
expectTypeOf(new User("test", 1)).toHaveProperty("getName").returns.toBeString();

// Constructor instance type
expectTypeOf(User).instance.toEqualTypeOf<{
  name: string;
  getName(): string;
}>();