@total-typescript/shoehorn

repository·main·Indexed 20 days ago

https://github.com/total-typescript/shoehorn

A testing utility for TypeScript that allows developers to safely pass partial or incorrect data into functions expecting strict types without using unsafe 'as' assertions. It provides functions like fromPartial for deep partial objects, fromAny for bypassing strict checks with autocomplete, and fromExact for enforcing full type compliance, along with type utilities like NoInfer and PartialDeep.

Tokens
2K
Snippets
9
Records
10
Agent score
70%

What's inside @total-typescript/shoehorn

  1. What is shoehorn and when should I use it?

    main

    shoehorn is a utility designed to let you pass partial or incorrect data into functions during testing while maintaining TypeScript safety and autocomplete.

    It solves the problem of using the as keyword in tests, which is often discouraged and requires manual type assertion or 'double-casting' (as unknown as Type) when testing incorrect data.

    Use cases include:

    • Legacy codebases: When you cannot immediately refactor large, overly-broad types.
    • Third-party libraries: When you cannot modify external types and want to avoid unnecessary wrapper functions.
  2. Use fromAny to pass any data with autocomplete

    main

    The fromAny function allows you to pass any value to a slot while still providing autocomplete based on the original expected type.

    Warning: Unlike fromPartial, fromAny will not fail if you pass data that does not match the expected type. This makes it useful for testing how your code handles invalid or unexpected input.

    import { fromAny } from "@total-typescript/shoehorn";
    
    type Request = {
      body: {
        id: string;
      };
    };
    
    const requiresRequest = (request: Request) => {};
    
    // Works: even with incorrect types (id is a number instead of string)
    requiresRequest(
      fromAny({
        body: {
          id: 124123,
        },
      }),
    );
    
    // Works: passing a completely different type
    requiresRequest(fromAny("1234123"));
  3. Use fromExact to enforce full type compliance

    main

    The fromExact function is a convenience method that forces you to pass all properties of a required type. This is useful when you want to switch between fromPartial or fromAny and ensure that your test data is strictly complete and valid.

    import { fromExact } from "@total-typescript/shoehorn";
    
    type Request = {
      body: {
        id: string;
      };
      // ... oodles of other properties
    };
    
    const requiresRequest = (request: Request) => {};
    
    // Fails: because we are not providing all properties of Request
    requiresRequest(
      fromExact({
        body: {
          id: "123",
        },
      }),
    );
  4. Use fromPartial to pass deep partial data

    main

    The fromPartial function allows you to pass a deep partial object to a slot expecting a specific type. It provides type safety by ensuring that the data you provide matches the structure of the expected type (even if it is incomplete).

    Note: It will fail at compile-time if the provided object has no properties in common with the expected type.

    import { fromPartial } from "@total-typescript/shoehorn";
    
    type Request = {
      body: {
        id: string;
      };
      // ... other properties
    };
    
    const requiresRequest = (request: Request) => {};
    
    // Works: only providing the necessary nested property
    requiresRequest(
      fromPartial({
        body: {
          id: "123",
        },
      }),
    );
    
    // Fails: type mismatch
    // requiresRequest(fromPartial("1234123"));
  5. Create deep partial mocks with fromPartial()

    main

    Use fromPartial<T>(mock) when you want to provide a mock object that only implements a subset of the properties required by type T. It accepts a PartialDeep<NoInfer<T>>, allowing you to omit nested properties throughout the object structure. This is useful for testing components or functions that require complex objects where you only care about a few specific fields.

    // Example usage:
    // If T is { user: { id: string, name: string } }
    // fromPartial allows: { user: { id: '123' } }
    const mock = fromPartial<MyComplexType>({ 
      user: { id: '123' } 
    });
  6. Allow any value in mocks with fromAny()

    main

    Use fromAny<T, U>(mock) when you want to bypass strict type checking for a mock value, effectively allowing any or a wider type U to be passed where type T is expected. This is helpful when you want to pass a dummy value (like null or an empty object) that doesn't strictly satisfy T, while still maintaining autocomplete for T if you decide to provide a valid value later.

    // Example usage:
    // Allows passing something that isn't strictly T
    const mock = fromAny<MyType, any>(someLooseValue);
  7. Enforce exact types with fromExact()

    main

    Use fromExact<T>(mock) when you want to ensure that the value passed to a mock slot matches the required type T exactly. Unlike fromPartial or fromAny, this function provides no loosening of constraints; it acts as a type assertion that requires the input to satisfy the full shape of T.

    // Example usage:
    // Requires the full object structure of MyType
    const mock = fromExact<MyType>({ 
      user: { id: '123', name: 'John' } 
    });
  8. Use NoInfer to prevent type inference in generic constraints

    main

    The NoInfer<T> type utility is used to prevent TypeScript from using a specific argument to infer a generic type parameter. This is particularly useful in functions where you want a generic type to be determined by one argument, but you want to pass a second argument that matches that type without allowing the second argument to influence the inference of the generic.

    type NoInfer<T> = [T][T extends any ? 0 : never];
  9. Use PartialDeep for deep partial type transformations

    main

    The PartialDeep<T> type utility creates a version of type T where every property, including nested objects and array elements, is optional. This is useful for creating mock data or partial updates for complex, deeply nested structures.

    Key behaviors:

    • Objects: All keys become optional, and their values are recursively wrapped in PartialDeep.
    • Arrays: Elements within arrays are wrapped in PartialDeep.
    • Functions: Functions are treated as objects (via PartialDeepObject) or undefined.
    export type PartialDeep<T> = T extends (...args: any[]) => any
      ? PartialDeepObject<T> | undefined
      : T extends object
      ? T extends ReadonlyArray<infer ItemType>
        ? ItemType[] extends T
          ? readonly ItemType[] extends T
            ? ReadonlyArray<PartialDeep<ItemType | undefined>>
            : Array<PartialDeep<ItemType | undefined>>
          : PartialDeepObject<T>
        : PartialDeepObject<T>
      : T;
    
    export type PartialDeepObject<ObjectType extends object> = {
      [KeyType in keyof ObjectType]?: PartialDeep<ObjectType[KeyType]>;
    };