ts-type-forge
    Preparing search index...

    ts-type-forge

    npm version

    npm downloads License

    ts-type-forge is a comprehensive TypeScript type utility library that provides powerful type-level operations with zero runtime cost. It enhances TypeScript development by offering advanced type manipulations, strict type checking utilities, and comprehensive type safety features.

    This library offers a comprehensive suite of type-level utilities, including:

    • Advanced Type Utilities: Enhanced versions of built-in types like StrictExclude, StrictOmit, ReadonlyRecord, and many more.
    • Compile-Time Type Checking: Assert type relationships at compile time with comprehensive condition types.
    • Branded Types: Extensive collection of branded number types (Int, Uint, SafeInt, FiniteNumber, etc.) for enhanced type safety.
    • Array and Tuple Utilities: Type-safe operations with List and Tuple namespaces for complex array manipulations.
    • Record Manipulation: Deep operations like DeepReadonly, DeepPartial, and advanced path-based record updates.
    • Type-Level Arithmetic: Integer operations, ranges (UintRange), and mathematical type computations.
    • Global Type Availability: No need for import statements when using Triple-Slash Directives.
    • Zero Runtime Cost: Pure type-level operations with no runtime dependencies.
    • Comprehensive Testing: Thoroughly tested for type correctness with custom type-testing utilities.
    npm add --save-dev ts-type-forge
    

    Or with other package managers:

    # Yarn
    yarn add --dev ts-type-forge

    # pnpm
    pnpm add --save-dev ts-type-forge

    ts-type-forge works best with strict TypeScript settings:

    {
    "compilerOptions": {
    "strict": true, // important
    "noUncheckedIndexedAccess": true, // important
    "noPropertyAccessFromIndexSignature": true, // important
    "noFallthroughCasesInSwitch": true,
    "noImplicitOverride": true,
    "noImplicitReturns": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "allowUnreachableCode": false,
    "allowUnusedLabels": false,
    "exactOptionalPropertyTypes": false
    }
    }

    There are two ways to use the types provided by ts-type-forge:

    1. Explicit Imports (Recommended — side-effect free): Import the types you need by name. Only the imported types come into scope; nothing else from ts-type-forge is added to the global namespace, so the package can be loaded without affecting the rest of your project.

      // src/types/dice.ts
      import { type UintRange } from 'ts-type-forge';

      export type DiceValue = UintRange<1, 7>; // 1 | 2 | 3 | 4 | 5 | 6
    2. Triple-Slash Directive (opt-in to ambient access): When you prefer ambient access, add /// <reference types="ts-type-forge/global" /> to any .ts file in your project (e.g., globals.d.ts or at the top of a frequently used file included in the tsconfig.json). This makes every type provided by ts-type-forge globally available throughout your project — useful for prototyping or for projects that already rely on ambient typings.

      // src/globals.d.ts or any other .ts file
      /// <reference types="ts-type-forge/global" />

      // src/types/dice.ts
      // No import needed
      export type DiceValue = UintRange<1, 7>; // 1 | 2 | 3 | 4 | 5 | 6

    Essential type-level conditional logic for advanced type operations.

    • Type Equality - TypeEq for exact type matching
    • Type Extensions - TypeExtends for subtype relationships
    • Union Detection - IsUnion for union type identification
    • Never Detection - IsNever for never type checking

    Advanced object type manipulations with strict type safety.

    • Strict Operations - StrictOmit, StrictPick, StrictExclude with key validation
    • Deep Operations - DeepReadonly, DeepPartial, DeepRequired
    • Partial Utilities - PartiallyPartial, PartiallyOptional, PartiallyRequired
    • Record Paths - RecordPaths, RecordValueAtPath for type-safe property access

    Comprehensive branded types for enhanced numeric type safety.

    • Basic Types - Int, Uint, SafeInt, FiniteNumber
    • Range Types - Int16, Int32, Uint16, Uint32
    • Constraint Types - PositiveInt, NonZeroInt, NonNegativeInt
    • Floating Point - Float32, Float64 with proper constraints

    Type-safe array and tuple utilities with functional programming patterns.

    • Array Types - NonEmptyArray, ArrayOfLength, ArrayAtLeastLen
    • List Namespace - Comprehensive list operations (Head, Tail, Take, Skip, etc.)
    • Tuple Namespace - Type-safe tuple manipulations with compile-time guarantees

    Mathematical operations performed entirely at the type level.

    • Integer Operations - Increment, Decrement, AbsoluteValue
    • Ranges - UintRange, UintRangeInclusive for precise numeric constraints
    • Comparisons - Max, Min for type-level comparisons

    Pre-defined type constants for common use cases.

    • Basic Constants - Primitive, FalsyValue, UnknownRecord
    • Web Types - HTTPRequestMethod for web development
    • Numeric Enums - MonthEnum, DateEnum, HoursEnum, etc.

    Here are detailed examples showcasing the power of ts-type-forge's type utilities.

    For a comprehensive list of all available types and their detailed documentation, please refer to the API Reference section.

    The type utilities allow you to perform complex type checking and assertions at compile time.

    // No import needed if using triple-slash directive
    // import type { TypeEq, TypeExtends } from 'ts-type-forge'; // if importing explicitly

    type User = { id: number; name: string };

    type Admin = { id: number; name: string; role: 'admin' };

    // Check exact type equality
    type IsExactMatch = TypeEq<User, Admin>; // false

    type IsSameType = TypeEq<User, User>; // true

    // Check type extension relationships
    type AdminExtendsUser = TypeExtends<Admin, User>; // true

    type UserExtendsAdmin = TypeExtends<User, Admin>; // false

    // Use in conditional types
    type GetUserType<T> =
    TypeExtends<T, Admin> extends true
    ? 'admin'
    : TypeExtends<T, User> extends true
    ? 'user'
    : 'unknown';

    type AdminType = GetUserType<Admin>; // 'admin'

    type UserType = GetUserType<User>; // 'user'

    Transform nested object types with precise control over mutability and optionality.

    type Config = {
    port: number;
    database: {
    host: string;
    port: number;
    credentials?: {
    user: string;
    pass: string;
    };
    };
    features: string[];
    };

    // Create a type where all properties, nested or not, are readonly
    type ReadonlyConfig = DeepReadonly<Config>;

    const config: ReadonlyConfig = {
    port: 8080,
    database: {
    host: 'localhost',
    port: 5432,
    credentials: {
    user: 'admin',
    pass: 'secret',
    },
    },
    features: ['featureA', 'featureB'],
    };

    // @ts-expect-error Cannot assign to 'port' because it is a read-only property
    config.port = 8081;

    // @ts-expect-error Cannot assign to 'host' because it is a read-only property
    config.database.host = 'remote';

    // @ts-expect-error Property 'push' does not exist on type 'readonly string[]'
    config.features.push('featureC');

    // Create a type where all properties are optional (useful for partial updates)
    type PartialConfig = DeepPartial<Config>;

    const partialUpdate: PartialConfig = {
    database: {
    host: 'new-host', // Only update specific fields
    // port and credentials are optional
    },
    // port and features are optional
    };

    Enhanced versions of built-in Omit utility that provide compile-time key validation.

    type UserProfile = Readonly<{
    id: string;
    username: string;
    email: string;
    lastLogin: Date;
    bio?: string;
    }>;

    // StrictOmit ensures keys actually exist in the source type
    type UserCreationData = StrictOmit<UserProfile, 'id' | 'lastLogin'>;

    expectType<
    UserCreationData,
    // Result:
    Readonly<{
    username: string;
    email: string;
    bio?: string | undefined;
    }>
    >('=');

    const newUser: UserCreationData = {
    username: 'jane_doe',
    email: 'jane@example.com',
    bio: 'Software Developer', // Optional
    };

    // @ts-expect-error 'nonExistentKey' doesn't exist:
    type InvalidOmit = StrictOmit<UserProfile, 'id' | 'nonExistentKey'>;

    Guarantee array constraints and perform type-safe operations on collections.

    type Post = Readonly<{
    title: string;
    content: string;
    }>;

    // A blog must have at least one post
    type Blog = Readonly<{
    name: string;
    posts: NonEmptyArray<Post>; // Ensures posts array is never empty
    }>;

    const myBlog: Blog = {
    name: 'My Tech Journey',
    posts: [
    // This array must have at least one element
    { title: 'First Post', content: 'Hello world!' },
    { title: 'Understanding TypeScript', content: '...' },
    ],
    };

    // This would cause a type error:
    const emptyBlog: Blog = {
    name: 'Empty Thoughts',
    // @ts-expect-error Source has 0 element(s) but target requires 1
    posts: [],
    };

    const getFirstPostTitle = (posts: NonEmptyArray<Post>): string =>
    posts[0].title; // Safe to access posts[0] - guaranteed to exist

    // Advanced List operations at the type level
    type NumberList = readonly [1, 2, 3, 4, 5];

    type FirstElement = List.Head<NumberList>; // 1

    type LastElement = List.Last<NumberList>; // 5

    type WithoutFirst = List.Tail<NumberList>; // readonly [2, 3, 4, 5]

    type FirstThree = List.Take<3, NumberList>; // readonly [1, 2, 3]

    type Reversed = List.Reverse<NumberList>; // readonly [5, 4, 3, 2, 1]

    // Combine operations
    type LastThreeReversed = List.Reverse<List.TakeLast<3, NumberList>>; // readonly [5, 4, 3]

    Safely represent and work with JSON data structures.

    const jsonString =
    '{"name": "Alice", "age": 30, "isAdmin": false, "tags": ["user", "active"], "metadata": null}';

    try {
    // Cast the result of JSON.parse to JsonValue for type safety
    const parsedData = JSON.parse(jsonString) as JsonValue;

    // Use type guards to safely work with parsed data
    if (
    typeof parsedData === 'object' &&
    parsedData !== null &&
    !Array.isArray(parsedData)
    ) {
    // parsedData is now known to be JsonObject
    const jsonObj = parsedData as JsonObject;

    console.log(jsonObj['name']); // Access properties safely

    if (typeof jsonObj['age'] === 'number') {
    console.log(`Age: ${jsonObj['age']}`);
    }

    if (Array.isArray(jsonObj['tags'])) {
    for (const tag of jsonObj['tags']) {
    if (typeof tag === 'string') {
    console.log(`Tag: ${tag}`);
    }
    }
    }
    } else if (Array.isArray(parsedData)) {
    // parsedData is a JSON array
    for (const item of parsedData) {
    console.log(item);
    }
    }
    } catch (error) {
    console.error('Failed to parse JSON:', error);
    }

    // Define API response types using JsonValue
    type ApiResponse = JsonObject &
    Readonly<{
    status: 'success' | 'error';
    data?: JsonValue;
    message?: string;
    }>;

    Define exact numeric constraints and enhance type safety with branded number types.

    /**
    * Parse integer with constrained radix parameter
    * @param str A string to convert into a number
    * @param radix A value between 2 and 36 that specifies the base
    */
    export const parseInteger = (str: string, radix?: UintRange<2, 37>): number =>
    Number.parseInt(str, radix);

    // Alternative using inclusive range
    export const parseIntegerInclusive = (
    str: string,
    radix?: UintRangeInclusive<2, 36>,
    ): number => Number.parseInt(str, radix);

    // Valid usages:
    parseInteger('10'); // radix defaults to 10

    parseInteger('10', 2); // Binary

    parseInteger('255', 16); // Hexadecimal

    parseInteger('123', 36); // Maximum base

    // Invalid usages (TypeScript will error):
    // @ts-expect-error Argument of type '1' is not assignable to parameter of type 'UintRange<2, 37> | undefined'
    parseInteger('10', 1);

    // @ts-expect-error Argument of type '37' is not assignable to parameter of type 'UintRange<2, 37> | undefined'
    parseInteger('10', 37);

    // Branded types for additional safety
    type UserId = Brand<number, 'UserId'>;

    type ProductId = Brand<number, 'ProductId'>;

    // Create branded values (you would typically have constructor functions)
    declare const userId: UserId;

    declare const productId: ProductId;

    // Type-safe functions that can't mix up IDs
    const getUserById = (id: UserId): User | undefined => {
    /* ... */

    return undefined;
    };

    const getProductById = (id: ProductId): Product | undefined => {
    /* ... */

    return undefined;
    };

    // @ts-expect-error Argument of type 'ProductId' is not assignable to parameter of type 'UserId'
    getUserById(productId);

    The library is organized into logical modules for easy navigation and understanding:

    • condition/: Type predicates like TypeEq, TypeExtends, IsUnion, IsNever for conditional type logic.
    • record/: Object type utilities including StrictOmit, DeepReadonly, RecordPaths, and partial operations.
    • branded-types/: Comprehensive branded number types (Int, Uint, SafeInt, FiniteNumber, etc.) with range constraints.
    • tuple-and-list/: Array and tuple operations with List and Tuple namespaces for type-safe manipulations.
    • type-level-integer/: Mathematical operations like Increment, UintRange, AbsoluteValue performed at the type level.
    • constants/: Pre-defined constants like Primitive, FalsyValue, HTTPRequestMethod, and enum types.
    • others/: Utility types like JsonValue, Mutable, WidenLiteral, and helper functions.
    • Type Safety: All utilities are designed with TypeScript's advanced type system, providing compile-time guarantees.
    • Zero Runtime Cost: Pure type-level operations with no runtime dependencies or overhead.
    • Comprehensive Coverage: From basic utilities to advanced type manipulations for complex scenarios.
    • Global Availability: Use triple-slash directives to make types available without explicit imports.
    • Extensive Testing: All utilities are thoroughly tested with custom type-testing framework.
    • Strict Validation: Enhanced versions of built-in types with compile-time key validation.

    For detailed information on all types, see the Full API Reference.

    • This library is type-level only with zero runtime dependencies and no runtime cost.
    • All types are designed to work seamlessly with TypeScript's strict mode settings.
    • The library supports both explicit imports and global type availability via triple-slash directives.
    • Custom type-testing utilities ensure all operations work correctly at compile time.

    While ts-type-forge provides powerful compile-time type utilities, combining it with ts-data-forge unlocks runtime type validation capabilities that make your TypeScript applications even more robust.

    ts-data-forge complements ts-type-forge by providing:

    • Type Guard Functions: Runtime validation for all the branded types defined in ts-type-forge
    • Type Assertions: Throw errors when values don't match expected types
    • Type Predicates: Safely narrow types at runtime with is* functions
    • JSON Validation: Runtime validation for JsonValue types
    • Array Guards: Validate NonEmptyArray and other array constraints at runtime
    /// <reference types="ts-type-forge/global" />

    // Runtime validation with ts-data-forge
    import {
    isUint,
    expectType,
    assertNonEmptyArray,
    parseJsonValue,
    isRecord,
    hasKey,
    } from 'ts-data-forge';

    const numbers: readonly number[] = [1, 2, 3, 4, 5, 2, 3];

    // Type-safe length checking
    if (Arr.isArrayAtLeastLength(numbers, 2)) {
    // numbers is now guaranteed to have at least 3 elements
    expectType<typeof numbers, ArrayAtLeastLen<2, number>>('=');
    console.log(numbers[1]); // Array access to index 0, 1 is now safe even with noUncheckedIndexedAccess enabled
    }

    // Safe JSON parsing with type validation
    const jsonString = '{"count": 42, "items": [1, 2, 3]}';
    const data: JsonValue = parseJsonValue(jsonString); // Validates at runtime

    // Use the data with confidence
    if (isRecord(data) && hasKey(data, 'count')) {
    console.log(data.count); // Safe access
    }
    1. Complete Type Safety: Compile-time guarantees with ts-type-forge + runtime validation with ts-data-forge
    2. API Boundary Protection: Validate external data (API responses, user input) at runtime
    3. Developer Experience: Same type names and conventions across both libraries
    4. Zero Runtime Cost Option: Use only ts-type-forge when runtime validation isn't needed
    5. Progressive Enhancement: Start with type-level safety, add runtime checks where needed

    Install both libraries to get the full TypeScript type safety experience:

    npm add ts-data-forge
    npm add -D ts-type-forge

    This library requires TypeScript version 4.8 or higher for full compatibility with advanced type features.

    Contributions are welcome! Please see the repository's contribution guidelines for detailed information on how to contribute to this project.

    Apache-2.0