ts-type-forge
v2.3.0
Published
[](https://www.npmjs.com/package/ts-type-forge) [](https://www.npmjs.com/package/ts-type-forge) [ for enhanced type safety. - Array and Tuple Utilities: Type-safe operations with
ListandTuplenamespaces 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.
Installation
npm add --save-dev ts-type-forgeOr with other package managers:
# Yarn
yarn add --dev ts-type-forge
# pnpm
pnpm add --save-dev ts-type-forgeTypeScript Configuration
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
}
}How to use
There are two ways to use the types provided by ts-type-forge:
Triple-Slash Directive (Recommended for global availability): Add
/// <reference types="ts-type-forge" />to any.tsfile in your project (e.g.,globals.d.tsor at the top of a frequently used file). This makes all types fromts-type-forgeglobally available without needing explicit imports.// src/globals.d.ts or any other .ts file /// <reference types="ts-type-forge" /> // src/types/dice.ts // No import needed export type DiceValue = UintRange<1, 7>; // 1 | 2 | 3 | 4 | 5 | 6Explicit Imports: Alternatively, you can import types explicitly if you prefer more granular control or are not using triple-slash directives.
// src/types/dice.ts import { type UintRange } from 'ts-type-forge'; export type DiceValue = UintRange<1, 7>; // 1 | 2 | 3 | 4 | 5 | 6
Core Modules
🎯 Type Conditions and Predicates
Essential type-level conditional logic for advanced type operations.
- Type Equality -
TypeEqfor exact type matching - Type Extensions -
TypeExtendsfor subtype relationships - Union Detection -
IsUnionfor union type identification - Never Detection -
IsNeverfor never type checking
🔧 Record and Object Types
Advanced object type manipulations with strict type safety.
- Strict Operations -
StrictOmit,StrictPick,StrictExcludewith key validation - Deep Operations -
DeepReadonly,DeepPartial,DeepRequired - Partial Utilities -
PartiallyPartial,PartiallyOptional,PartiallyRequired - Record Paths -
RecordPaths,RecordValueAtPathfor type-safe property access
🔢 Branded Number Types
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,Float64with proper constraints
📋 Array and Tuple Operations
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
🧮 Type-Level Arithmetic
Mathematical operations performed entirely at the type level.
- Integer Operations -
Increment,Decrement,AbsoluteValue - Ranges -
UintRange,UintRangeInclusivefor precise numeric constraints - Comparisons -
Max,Minfor type-level comparisons
🌐 Constants and Primitives
Pre-defined type constants for common use cases.
- Basic Constants -
Primitive,FalsyValue,UnknownRecord - Web Types -
HTTPRequestMethodfor web development - Numeric Enums -
MonthEnum,DateEnum,HoursEnum, etc.
Usage Examples
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.
1. Type-Level Conditional Logic with TypeEq and TypeExtends
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'2. Deep Object Manipulation with DeepReadonly and DeepPartial
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
};3. Strict Type Operations with StrictOmit
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: '[email protected]',
bio: 'Software Developer', // Optional
};
// @ts-expect-error 'nonExistentKey' doesn't exist:
type InvalidOmit = StrictOmit<UserProfile, 'id' | 'nonExistentKey'>;4. Array Type Safety with NonEmptyArray and List Operations
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]5. Type-Safe JSON Handling with JsonValue
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;
}>;6. Precise Numeric Ranges with UintRange and Branded Types
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
function getUserById(id: UserId): User | undefined {
/* ... */
return undefined;
}
function getProductById(id: ProductId): Product | undefined {
/* ... */
return undefined;
}
// @ts-expect-error Argument of type 'ProductId' is not assignable to parameter of type 'UserId'
getUserById(productId);Modules Overview
The library is organized into logical modules for easy navigation and understanding:
condition/: Type predicates likeTypeEq,TypeExtends,IsUnion,IsNeverfor conditional type logic.record/: Object type utilities includingStrictOmit,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 withListandTuplenamespaces for type-safe manipulations.type-level-integer/: Mathematical operations likeIncrement,UintRange,AbsoluteValueperformed at the type level.constants/: Pre-defined constants likePrimitive,FalsyValue,HTTPRequestMethod, and enum types.others/: Utility types likeJsonValue,Mutable,WidenLiteral, and helper functions.
Key Benefits
- 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.
API Reference
For detailed information on all types, see the Full API Reference.
Overview of All Types (with source code links)
- src/branded-types/bigint.d.mts
- src/branded-types/brand.d.mts
- src/branded-types/core.d.mts
- src/branded-types/finite-number.d.mts
- src/branded-types/float.d.mts
- src/branded-types/int.d.mts
- src/branded-types/int16.d.mts
- src/branded-types/int32.d.mts
- src/branded-types/safe-int.d.mts
- src/branded-types/small-int.d.mts
- src/branded-types/uint16.d.mts
- src/branded-types/uint32.d.mts
- src/condition/eq.d.mts
- src/condition/extends.d.mts
- src/condition/is-fixed-length-list.d.mts
- src/condition/is-never.d.mts
- src/condition/is-union.d.mts
- src/constants/alphabet.d.mts
- src/constants/falsy-value.d.mts
- src/constants/int-enum.d.mts
- src/constants/primitive.d.mts
- src/constants/record.d.mts
- src/constants/web.d.mts
- src/index.d.mts
- src/others/bivariant-hack.d.mts
- src/others/boolean.d.mts
- src/others/json.d.mts
- src/others/mutable.d.mts
- src/others/utils.d.mts
- src/others/widen-literal.d.mts
- src/record/deep.d.mts
- src/record/partial.d.mts
- src/record/record-path.d.mts
- src/record/std.d.mts
- src/tuple-and-list/array.d.mts
- src/tuple-and-list/index-of-tuple.d.mts
- src/tuple-and-list/list.d.mts
- src/tuple-and-list/make-tuple.d.mts
- src/tuple-and-list/tuple.d.mts
- Tuple.Head
- Tuple.Last
- Tuple.ButLast
- Tuple.Tail
- Tuple.Reverse
- Tuple.Take
- Tuple.Skip
- Tuple.TakeLast
- Tuple.SkipLast
- Tuple.SetAt
- Tuple.Flatten
- Tuple.Concat
- Tuple.Zip
- Tuple.Partition
- Tuple.ReverseImpl
- Tuple.TakeImpl
- Tuple.SkipImpl
- Tuple.TakeLastImpl
- Tuple.SkipLastImpl
- Tuple.SetAtImpl
- Tuple.FlattenImpl
- Tuple.ConcatImpl
- Tuple.PartitionImpl
- src/type-level-integer/abs.d.mts
- src/type-level-integer/increment.d.mts
- src/type-level-integer/index-type.d.mts
- src/type-level-integer/max.d.mts
- src/type-level-integer/min.d.mts
- src/type-level-integer/seq.d.mts
- src/type-level-integer/uint-range.d.mts
Important Notes
- 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.
Runtime Type Guards with ts-data-forge
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
JsonValuetypes - Array Guards: Validate
NonEmptyArrayand other array constraints at runtime
Example: Combining Type-Level and Runtime Safety
/// <reference types="ts-type-forge" />
// 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
}Benefits of Using Both Libraries Together
- Complete Type Safety: Compile-time guarantees with ts-type-forge + runtime validation with ts-data-forge
- API Boundary Protection: Validate external data (API responses, user input) at runtime
- Developer Experience: Same type names and conventions across both libraries
- Zero Runtime Cost Option: Use only ts-type-forge when runtime validation isn't needed
- 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-forgeCompatibility Notes
This library requires TypeScript version 4.8 or higher for full compatibility with advanced type features.
Contributing
Contributions are welcome! Please see the repository's contribution guidelines for detailed information on how to contribute to this project.
License
Apache-2.0
