ts-essentials vs ts-extras vs ts-toolbelt
Advanced TypeScript Utility Libraries: Architecture, Safety, and Selection
ts-essentialsts-extrasts-toolbelt

Advanced TypeScript Utility Libraries: Architecture, Safety, and Selection

ts-essentials, ts-extras, and ts-toolbelt are utility libraries designed to extend TypeScript's built-in type system with advanced helper types. ts-essentials focuses on providing robust, battle-tested types for common edge cases like deep partials, exact objects, and safe Omit operations, prioritizing stability and ease of use. ts-extras offers a curated set of practical types and runtime helpers that bridge the gap between type-level logic and actual JavaScript execution, often solving specific pain points in modern frontend architectures. ts-toolbelt is a comprehensive, functional-programming-inspired toolkit containing hundreds of complex types for manipulating lists, strings, numbers, and types at a granular level, suitable for heavy metaprogramming.

Npm Package Weekly Downloads Trend

3 Years

Github Stars Ranking

Stat Detail

Package
Downloads
Stars
Size
Issues
Publish
License
ts-essentials04,076263 kB173 months agoMIT
ts-extras080788.6 kB02 months agoMIT
ts-toolbelt07,150-846 years agoApache-2.0

Deep Dive: ts-essentials vs ts-extras vs ts-toolbelt

When working with TypeScript in large-scale frontend applications, the built-in utility types like Partial, Pick, and Omit often aren't enough. You eventually hit walls where you need to make types deeply optional, enforce exact shapes to prevent excess properties, or manipulate types in ways that feel like writing code inside your types. This is where ts-essentials, ts-extras, and ts-toolbelt come in. While they all aim to supercharge your type system, they serve very different roles in an architecture.

🛡️ Core Philosophy: Stability vs. Practicality vs. Power

The most important distinction lies in what each library tries to solve.

ts-essentials is the conservative choice. It focuses on fixing holes in TypeScript's standard library. Its goal is to provide types that "should have been built-in" but aren't, like DeepPartial or Exact. It prioritizes long-term stability and readability.

ts-extras sits in the middle. It provides practical types that solve specific, real-world problems developers face daily, often including runtime helpers alongside types. It is less about pure type manipulation and more about getting work done efficiently.

ts-toolbelt is the powerhouse. It treats types as data to be manipulated with functional programming principles. It contains hundreds of tools for list manipulation, string parsing, and number arithmetic at the type level. It is incredibly powerful but comes with significant complexity.

🔍 Feature Comparison: Deep Partial Types

One of the most common needs is making every property in a nested object optional. Native Partial<T> only works on the top level.

ts-essentials provides DeepPartial. It is widely used, well-tested, and handles most standard cases reliably.

import { DeepPartial } from 'ts-essentials';

interface Config {
  database: {
    host: string;
    port: number;
  };
}

// All nested properties become optional
const myConfig: DeepPartial<Config> = {
  database: {
    host: "localhost"
    // port is optional now
  }
};

ts-extras also offers a DeepPartial type, often optimized for specific use cases found in modern frameworks, ensuring compatibility with complex generic patterns.

import { DeepPartial } from 'ts-extras';

interface State {
  user: {
    profile: {
      name: string;
    };
  };
}

// Works similarly but may handle edge cases differently based on library version
const partialState: DeepPartial<State> = {
  user: {
    profile: {}
  }
};

ts-toolbelt provides Deep.Partial, part of its namespaced architecture. It allows for extreme customization, such as stopping recursion at certain types, but requires more verbose syntax.

import { Deep } from 'ts-toolbelt';

interface Settings {
  theme: {
    colors: {
      primary: string;
    };
  };
}

// Explicitly calls the Deep namespace
const partialSettings: Deep.Partial<Settings> = {
  theme: {
    colors: {
      // primary is optional
    }
  }
};

🎯 Feature Comparison: Exact Object Types

TypeScript sometimes allows extra properties when passing objects to functions, which can cause bugs. An "Exact" type ensures you pass only the defined properties.

ts-essentials exports Exact specifically for this. It is a go-to solution for ensuring strict function arguments.

import { Exact } from 'ts-essentials';

function createUser(user: Exact<{ id: number; name: string }>) {
  // ...
}

// Error: 'age' is not allowed
createUser({ id: 1, name: "Alice", age: 30 }); 

ts-extras provides similar functionality, often tailored to work seamlessly with common patterns in React props or API responses, ensuring type safety without excessive boilerplate.

import { Exact } from 'ts-extras';

function renderWidget(props: Exact<{ width: number; height: number }>) {
  // ...
}

// Error: Extra property 'color' rejected
renderWidget({ width: 100, height: 50, color: "red" });

ts-toolbelt approaches this via its Any or Union checks, but does not have a single exported Exact type in the same simple form. You would typically construct this using lower-level primitives like Has or Keys, which increases complexity for a simple task.

// ts-toolbelt does not have a direct 'Exact' export like the others.
// You would need to build a custom constraint using lower-level tools,
// making it overkill for this specific scenario.

🧩 Feature Comparison: Complex Type Manipulation

This is where the libraries diverge sharply. If you need to manipulate strings, numbers, or lists at the type level, ts-toolbelt is in a league of its own.

ts-toolbelt excels here. It can split strings, calculate numbers, and merge lists entirely at compile time.

import { String, Number } from 'ts-toolbelt';

// Split a string literal type
type Parts = String.Split<"hello-world", "-">; 
// Result: ["hello", "world"]

// Add numbers at type level
type Sum = Number.Add<2, 3>; 
// Result: 5

ts-essentials does not attempt these kinds of operations. It sticks to structural changes (Partial, Required, Pick) rather than content manipulation.

// ts-essentials has no equivalent for string splitting or math.
// Attempting this would require custom, complex type definitions.

ts-extras also avoids deep metaprogramming. It focuses on types that help with data structure shaping rather than type-level computation.

// ts-extras does not provide type-level arithmetic or string parsing.
// It remains focused on practical application shapes.

⚠️ Maintenance and Deprecation Status

As of the latest checks, none of these packages are officially deprecated. However, their maintenance models differ.

  • ts-essentials is highly stable. Updates are infrequent because the core problems it solves (DeepPartial, Exact) don't change often. It is safe for long-term enterprise projects.
  • ts-extras is actively maintained with updates that align with modern TypeScript releases. It is a solid choice for new projects needing practical helpers.
  • ts-toolbelt is powerful but complex. While not deprecated, its heavy reliance on recursive types can sometimes cause performance issues in newer TypeScript versions or lead to longer compile times. Use it with caution in large codebases.

📊 Summary of Differences

Featurets-essentialsts-extrasts-toolbelt
Primary GoalFill standard library gapsPractical, real-world helpersAdvanced type metaprogramming
ComplexityLowMediumVery High
Deep Partial✅ DeepPartial✅ DeepPartial✅ Deep.Partial
Exact Types✅ Exact✅ Exact❌ (Manual construction)
Type Math/Strings❌❌✅ Extensive support
Best ForStandard AppsModern FrameworksLibrary Authors

💡 The Big Picture

Choosing the right library depends entirely on the complexity of your type needs.

If you are building a standard React application, a dashboard, or an enterprise platform, ts-essentials is your best friend. It gives you the superpowers you actually need—like making nested configs optional or enforcing strict props—without dragging in unnecessary complexity. It is the "batteries-included" approach for types.

If you find yourself needing slightly more specialized tools that bridge runtime and compile-time logic, or if you want a curated set of extras that feel modern and opinionated, ts-extras is a strong contender. It strikes a balance between utility and simplicity.

Reserve ts-toolbelt for when you are writing a library that others will use, or if you are building a domain-specific language (DSL) where types need to perform logic. For 95% of frontend development tasks, it is simply too much tool for the job. Using it for simple DeepPartial needs is like using a industrial crane to hang a picture frame—it works, but it's not the right tool.

Final Recommendation: Start with ts-essentials. It covers the vast majority of real-world scenarios with the least friction. Only reach for ts-toolbelt if you hit a wall that the other two cannot solve.

How to Choose: ts-essentials vs ts-extras vs ts-toolbelt

  • ts-essentials:

    Choose ts-essentials when you need reliable, standard utility types for everyday development tasks like making all properties optional recursively or enforcing exact object shapes. It is the best fit for teams that want to avoid the complexity of maintaining custom utility types while ensuring high compatibility across TypeScript versions without risking build breaks from overly experimental features.

  • ts-extras:

    Choose ts-extras if your project requires a blend of advanced type utilities and lightweight runtime helpers that solve specific, recurring problems in React or Node.js environments. This package is ideal when you want practical, opinionated solutions that are more specialized than ts-essentials but less overwhelming and abstract than the massive collection found in ts-toolbelt.

  • ts-toolbelt:

    Choose ts-toolbelt only when you are building complex type-level libraries, DSLs, or frameworks that require intense metaprogramming capabilities like type-level arithmetic, string manipulation, or deep conditional logic. Avoid this for standard application development, as its steep learning curve and heavy compile-time cost are unnecessary for typical business logic and can slow down your editor experience.

README for ts-essentials

ts-essentials

ts-essentials

All essential TypeScript types in one place 🤙

Version Downloads Build status Telegram Software License

Install

npm install --save-dev ts-essentials

👉 We require typescript>=4.5. If you're looking for support for older TS versions, please have a look at the TypeScript dependency table

👉 As we really want types to be stricter, we require enabled strictNullChecks in your project

API

ts-essentials is a set of high-quality, useful TypeScript types that make writing type-safe code easier.

Basic

Utility types

  • AsyncOrSync<Type> - Constructs a type with Type or PromiseLike<Type>
  • AsyncOrSyncType<Type> - Unwraps AsyncOrSync type
  • Dictionary<Type, Keys?> - Constructs a required object type which property keys are Keys (string by default) and which property values are Type
  • Merge<Object1, Object2> - Constructs a type by picking all properties from Object1 and Object2. Property values from Object2 override property values from Object1 when property keys are the same
  • MergeN<Tuple> - Constructs a type by merging objects with type Merge in tuple Tuple recursively
  • Newable<ReturnType> - Constructs a class type with constructor which has return type ReturnType
  • OmitNeverProperties<Type> - Constructs a type by picking all properties from type Type, which values don't equal to never
  • OmitProperties<Type, Value> - Constructs a type by picking all properties from type Type and removing those properties which values equal to Value
  • Opaque<Type, Token> - Constructs a type which is a subset of Type with a specified unique token Token
  • PathValue<Type, Path> - Constructs a path value for type Type and path Path
  • Paths<Type> - Constructs a union type by picking all possible paths for type Type
  • PickProperties<Type, Value> - Constructs a type by picking all properties from type Type which values equal to Value
  • PublicInterface<Type> - Constructs a type by extracting only the public members of Type, producing a plain object-compatible interface
  • RequireAtLeastOne<Type, Keys?> - Constructs a type with at least one required key from Keys (keyof Type by default) and other keys from Type which are not part of Keys
  • RequireAtMostOne<Type, Keys?> - Constructs a type with at most one required key from Keys (keyof Type by default) and other keys from Type which are not part of Keys
  • SafeDictionary<Type, Keys?> - Constructs an optional object type which property keys are Keys (string by default) and which property values are Type
  • UnionToIntersection<Union> - Constructs a intersection type from union type Union
  • ValueOf<Type> - Constructs a type for type Type and equals to a primitive for primitives, array elements for arrays, function return type for functions or object property values for objects
  • XOR<Type1, Type2, Type3?, ..., Type50?> - Construct a type which is assignable to either type Type1, Type2 but not both. Starting in ts-essentials@10, it supports up to 50 generic types.

Mark wrapper types

  • MarkOptional<Type, Keys> - Constructs a type by picking all properties from type Type where properties Keys are set as optional, meaning they aren't required
  • MarkReadonly<Type, Keys> - Constructs a type by picking all properties from type Type where properties Keys are set to readonly, meaning they cannot be reassigned
  • MarkRequired<Type, Keys> - Constructs a type by picking all properties from type Type where properties Keys are set as required
  • MarkWritable<Type, Keys> - Constructs a type by picking all properties from type Type where properties Keys remove readonly modifier, meaning they can be reassigned

Deep wrapper types

  • Buildable<Type> - Constructs a type by combining DeepPartial and DeepWritable, meaning all properties from type Type are recursively set as non-readonly and optional, meaning they can be reassigned and aren't required
  • DeepMarkOptional<Type, KeyPathUnion> - Constructs a type by picking all properties from type Type where properties by paths KeyPathUnion are set as optional. To mark properties optional on one level, use MarkOptional<Type, Keys>.
  • DeepMarkRequired<Type, KeyPathUnion> - Constructs a type by picking all properties from type Type where properties by paths KeyPathUnion are set as required. To mark properties required on one level, use MarkRequired<Type, Keys>.
  • DeepNonNullable<Type> - Constructs a type by picking all properties from type Type recursively and exclude null and undefined property values from all of them. To make properties non-nullable on one level, use NonNullable<Type>
  • DeepNullable<Type> - Constructs a type by picking all properties from type Type recursively and include null property values for all of them
  • DeepOmit<Type, Filter> - Constructs a type by picking all properties from type Type and removing properties which values are never or true in type Filter. If you'd like type Filter to be validated against a structure of Type, please use StrictDeepOmit<Type, Filter>.
  • DeepPartial<Type> - Constructs a type by picking all properties from type Type recursively and setting them as optional, meaning they aren't required. To make properties optional on one level, use Partial<Type>
  • DeepPick<Type, Filter> - Constructs a type by picking set of properties, which have property values never or true in type Filter, from type Type. If you'd like type Filter to be validated against a structure of Type, please use StrictDeepPick<Type, Filter>.
  • DeepReadonly<Type, OverrideDeepReadonlyOptions?> - Constructs a type by picking all properties from type Type recursively and setting readonly modifier, meaning they cannot be reassigned. To make properties readonly on one level, use Readonly<Type>
  • DeepRequired<Type> - Constructs a type by picking all properties from type Type recursively and setting as required. To make properties required on one level, use Required<Type>
  • DeepUndefinable<Type> - Constructs a type by picking all properties from type Type recursively and include undefined property values for all of them
  • DeepWritable<Type> - Constructs a type by picking all properties from type Type recursively and removing readonly modifier, meaning they can be reassigned. To make properties writable on one level, use Writable<Type>
  • StrictDeepOmit<Type, Filter> - Constructs a type by picking all properties from type Type and removing properties which values are never or true in type Filter. The type Filter is validated against a structure of Type.
  • StrictDeepPick<Type, Filter> - Constructs a type by picking set of properties, which have property values never or true in type Filter, from type Type. The type Filter is validated against a structure of Type.

Key types

  • OptionalKeys<Type> - Constructs a union type by picking all optional properties of object type Type
  • PickKeys<Type, Value> - Constructs a union type by picking all properties of object type Type which values are assignable to type Value
  • ReadonlyKeys<Type> - Constructs a union type by picking all readonly properties of object type Type, meaning their values cannot be reassigned
  • RequiredKeys<Type> - Constructs a union type by picking all required properties of object type Type
  • UnionKeys<UnionType> - Constructs a union type by picking all properties from all union members of UnionType
  • WritableKeys<Type> - Constructs a union type by picking all writable properties of object type Type, meaning their values can be reassigned

Type checkers

  • IsAny<Type> - Returns true when type Type is any. Otherwise returns false
  • IsExact<Type, Shape> - Returns Type when type Type and Shape are identical. Otherwise returns never
  • IsNever<Type> - Returns true when type Type is never. Otherwise returns false
  • IsUnknown<Type> - Returns true when type Type is unknown. Otherwise returns false
  • IsTuple<Type> - Returns Type when type Type is tuple. Otherwise returns never
  • NonEmptyObject<Object> - Returns Object when Object has at least one key. Otherwise returns never
  • NonUnion<Type> - Returns Type when Type is not an union. Otherwise returns never

Arrays and Tuples

Change case

Function types

Utility functions

⚠️ Make sure you add ts-essentials to your dependencies (npm install --save ts-essentials) to avoid runtime errors

Search

When one of utility types is known by a different name, kindly ask adding it here for the better search.

Declined

Built-in types

TypeScript provides several utility types to facilitate common type transformations. These utilities are available globally.

  • Awaited<Type> - This type is meant to model operations like await in async functions, or the .then() method on Promises - specifically, the way that they recursively unwrap Promises
  • Capitalize<StringType> - Converts the first character in the string to an uppercase equivalent
  • ConstructorParameters<Type> - Constructs a tuple or array type from the types of a constructor function type Type
  • Exclude<UnionType, ExcludedMembers> - Constructs a type by excluding from UnionType all union members that are assignable to ExcludedMembers
  • Extract<Type, Union> - Constructs a type by extracting from Type all union members that are assignable to Union
  • InstanceType<Type> - Constructs a type consisting of the instance type of a constructor function in Type
  • Lowercase<StringType> - Converts each character in the string to the lowercase equivalent
  • NonNullable<Type> - Constructs a type by excluding null and undefined from Type
  • NoInfer<Type> - Blocks inferences to the contained type. Other than blocking inferences, NoInfer<Type> is identical to Type
  • Omit<Type, Keys> - Constructs a type by picking all properties from Type and then removing Keys
  • OmitThisParameter<Type> - Removes the this parameter from Type. If Type has no explicitly declared this parameter, the result is simply Type. Otherwise, a new function type with no this parameter is created from Type. Generics are erased and only the last overload signature is propagated into the new function type.
  • Parameters<Type> - Constructs a tuple type from the types used in the parameters of a function type Type
  • Partial<Type> - Constructs a type with all properties of Type set to optional
  • Pick<Type, Keys> - Constructs a type by picking the set of properties Keys from Type
  • Readonly<Type> - Constructs a type with all properties of Type set to readonly, meaning the properties of the constructed type cannot be reassigned
  • Record<Keys, Type> - Constructs an object type whose property keys are Keys and whose property values are Type
  • Required<Type> - Constructs a type consisting of all properties of Type set to required
  • ReturnType<Type> - Constructs a type consisting of the return type of function type Type parameter
  • ThisParameterType<Type> - Extracts the type of the this parameter for a function type, or unknown if the function type has no this parameter
  • ThisType<Type> - This utility does not return a transformed type. Instead, it serves as a marker for a contextual this type. Note that the noImplicitThis flag must be enabled to use this utility
  • Uncapitalize<StringType> - Converts the first character in the string to a lowercase equivalent
  • Uppercase<StringType> - Converts each character in the string to the uppercase version

TypeScript dependency table

ts-essentialstypescript / type of dependency
^10.0.0^4.5.0 / peer optional
^9.4.0^4.1.0 / peer optional
^8.0.0^4.1.0 / peer
^5.0.0^3.7.0 / peer
^3.0.1^3.5.0 / peer
^1.0.1^3.2.2 / dev
^1.0.0^3.0.3 / dev

Limitations

Contributors

Special shout-out to active contributors:

And thanks goes to these wonderful people:

💻 - contributions, i.e. links to commits by the user on this project

Contributions of any kind welcome! Read more