Open-source project
piotrwitek/utility-types avatar
piotrwitek/utility-types

utility-types: TypeScript utility types without the copy-paste

Collection of utility types, complementing TypeScript built-in mapped types and aliases (think "lodash" for static types).

5,764 stars235 forksTypeScriptMIT

At a glance

What is it?
The piotrwitek/utility-types package adds object, union and Flow-compatible type operators on top of TypeScript's built-ins. It is type-level only, MIT licensed, and the last push was on 2026-05-09.
Who is it for?
Adopt utility-types when you keep rewriting the same DeepPartial, OptionalKeys or SetDifference definitions across projects, or when a Flow to TypeScript migration needs $Keys and $Shape shims. Skip it if TypeScript's built-in Partial, Pick, Omit and Record already cover your needs: the package adds names, not runtime behaviour.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 147 days ago.
What is it written in?
Mainly TypeScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 2, 2026, and from our analysis. They are not legal advice.

Editorial analysis

What utility-types adds to the TypeScript standard library

TypeScript ships a small set of mapped types: Partial, Required, Readonly, Pick, Omit, Exclude, Extract, NonNullable, ReturnType and InstanceType. They cover the common cases. The gap appears when you need to ask structural questions about a type, for example which keys are optional, which keys are mutable, or what the difference is between two object types. Teams usually answer those questions by pasting a private helper into a types.ts file, then pasting it again in the next repository. That duplication is what this package targets. The README describes it as a collection of utility types that complement the built-in mapped types and aliases, and the project's own framing is "think lodash for static types". The audience is TypeScript application and library authors who already reach for mapped types and want the missing operators under stable names. A second audience is teams migrating from Flow: the package ships a section of Flow's Utility Types, including $Keys, $Values, $ReadOnly, $Diff, $PropertyType, $ElementType, $Call, $Shape, $NonMaybeType, Class and mixed, so a codebase can keep recognisable names while the compiler changes underneath.

How the operators are organised: union, object and special

The README's table of contents splits the API into four groups. Union operators work on set-like relations between types: SetIntersection<A, B>, SetDifference<A, B>, SetComplement<A, A1>, SymmetricDifference<A, B>, plus the built-in Exclude and Extract and the package's NonUndefined<T>. Object operators inspect and transform keys: FunctionKeys<T>, NonFunctionKeys<T>, MutableKeys<T>, ReadonlyKeys<T>, RequiredKeys<T>, OptionalKeys<T> and UnionKeys<T> answer questions about a type's shape, while Optional<T, K>, DeepPartial<T>, Required<T, K>, DeepRequired<T>, DeepReadonly<T> and Mutable<T> transform it. PickByValue<T, ValueType> and OmitByValue<T, ValueType> select or drop properties by the type of their values, with PickByValueExact and OmitByValueExact as stricter variants; Intersection<T, U>, Diff<T, U>, Subtract<T, T1>, Overwrite<T, U>, Assign<T, U> and ValuesType<T> round out the group. Special operators cover PromiseType<T>, Unionize<T>, Brand<T, U> and UnionToIntersection<U>. The distinction between the plain and the Exact variants of the by-value operators is the kind of detail that matters in practice, and it is visible in the names rather than explained at length. There is also a deprecated getReturnOfExpression(), which the README says should be replaced by the type-level ReturnType from TypeScript v2.0 onward.

Installing utility-types and using it in a first file

The README gives two install commands, for npm and Yarn. The package has no runtime dependencies and the project states there is no runtime cost because it is type-level only, so the installed code contributes types and nothing to your bundle.

bash
npm install utility-types

After installation, import the operators you need from the package. The README's example for the isPrimitive type guard shows the shape of a consumer function that narrows a union of Primitive and Primitive[].

ts
import { Primitive, isPrimitive } from 'utility-types';

const consumer = (param: Primitive[] | Primitive): string => {
    if (isPrimitive(param)) {
        return String(param) + ' was Primitive';
    }
    const resultArray = param.map(consumer);
    return resultArray.reduce((comm, newV) => comm + newV, 'this was nested:');
};

Inside the if branch the parameter is narrowed to Primitive, and after it the compiler treats the value as Primitive[]. That narrowing is the whole point of the guard, and it happens at compile time. For a purely type-level operator such as Optional<T, K> or DeepPartial<T>, the import brings in a type alias you apply in annotations, with no value emitted to the output. If your project builds with an older compiler, check the compatibility table before installing: v3.x.x requires TypeScript v3.1 or newer, v2.x.x requires v2.8.1 or newer, and v1.x.x requires v2.7.2 or newer.

Where utility-types stops helping

Everything in this package is erased at compile time. It will not validate data that arrives at runtime, so a JSON payload typed as DeepRequired<T> is still unchecked until you parse and assert it yourself. The type guards isPrimitive, isFalsy and isNullish are the exception in the sense that they emit code, but they narrow values rather than validate a schema. The Flow compatibility section is a migration aid, not a compatibility layer: the README presents those aliases as a way to make the move to TypeScript easier, and it does not claim that Flow semantics carry over unchanged. Version history is worth reading before you plan an upgrade. The release list shows v3.10.0 in November 2019, v3.9.0 in October 2019, and then v3.11.0 in January 2024, so more than four years separate the last two minor releases. The repository's last push was on 2026-05-09, which is recent, but a long gap between published versions means you should read the changelog rather than assume continuous small changes. The README also does not document a rollback path or a deprecation policy for the types it exports, so treat the exported names as the contract you are depending on.

utility-types compared with writing your own mapped types

The realistic alternative is not another package. It is a local types.ts file where you write the mapped types you need. That approach has real advantages: no dependency, no version compatibility question, and full control over how each operator behaves at the edges. A four-line Optional<T, K> is not hard to write, and a team that only needs Partial, Pick and Omit should write nothing at all, because those are built into the compiler. The difference in approach shows up with the harder operators. ReadonlyKeys<T>, MutableKeys<T>, UnionKeys<T> and SymmetricDifference<A, B> are subtle to implement correctly across unions, intersections and optional properties, and the package's stated goal is quality, with type correctness tested through the dts-jest type-testing library. So the trade is a dependency in exchange for a tested implementation of operators you would otherwise debug yourself. The Flow section changes the calculation for teams mid-migration, since reproducing $Shape or $NonMaybeType by hand is a distraction from the migration itself. If you are already comfortable with the built-in mapped types and your helpers are short, the local file wins.

Licence, dependencies and what maintaining this costs you

The package is MIT licensed, and the licence file sits at the repository root. MIT permits commercial use, modification and redistribution provided the copyright notice and permission notice are included, but this is a description of the licence text, not legal advice for your situation. The dependency story is unusually clean: package.json lists an empty dependencies object, so installing utility-types pulls in nothing else at runtime. The devDependencies are a different matter and only concern contributors, listing jest, ts-jest, dts-jest, tslint, prettier, husky and typescript 3.7.2. The build script compiles with tsc against tsconfig.build.json into dist/, and the package's types field points at dist/index.d.ts. The project uses husky with a pre-push hook that runs prettier:fix, lint, tsc and test:update, so a contributor's push triggers the full check chain. Your ongoing cost is low if you pin a version and your compiler stays compatible, and higher if you track the master branch, because the gap between v3.10.0 and v3.11.0 shows that releases do not arrive on a predictable schedule. The README also points to IssueHunt for funding bug fixes and feature requests, which is a signal about how quickly an unfunded issue may move.

Editorial conclusion

Adopt utility-types when you keep rewriting the same DeepPartial, OptionalKeys or SetDifference definitions across projects, or when a Flow to TypeScript migration needs $Keys and $Shape shims. Skip it if TypeScript's built-in Partial, Pick, Omit and Record already cover your needs: the package adds names, not runtime behaviour. Before committing, check that your compiler satisfies the v3.x.x requirement of TypeScript v3.1 or newer, and read the source of any type you plan to build on, because the README documents usage examples rather than the semantics of each operator.

Frequently asked questions

What does utility-types mean in TypeScript?

It refers to a type that is defined in terms of another type, usually through mapped types, so that one type is derived from another instead of being written out by hand. This package collects such types, for example DeepPartial<T> and SetDifference<A, B>, and complements the ones TypeScript already provides.

What are utility types in TypeScript?

They are type-level helpers that transform or inspect other types. TypeScript ships some of them, such as Partial, Pick and Omit, and utility-types adds more, including OptionalKeys<T>, MutableKeys<T>, PickByValue<T, ValueType> and UnionToIntersection<U>.

How do I install utility-types?

The README gives npm install utility-types, or yarn add utility-types for Yarn users. The package has no runtime dependencies and the project states that it is type-level only, so it adds no runtime cost.

Which TypeScript version does utility-types v3.x.x require?

The README's compatibility notes state that v3.x.x supports TypeScript v3.1 and newer, v2.x.x supports v2.8.1 and newer, and v1.x.x supports v2.7.2 and newer. Check your compiler version before upgrading the package.

Does utility-types work for migrating a Flow codebase to TypeScript?

The README lists a Flow's Utility Types section with $Keys, $Values, $ReadOnly, $Diff, $PropertyType, $ElementType, $Call, $Shape, $NonMaybeType, Class and mixed, described as allowing easier migration to TypeScript. The README does not claim that the Flow and TypeScript versions behave identically.

Official sources

  1. Issues
  2. License: MIT
  3. piotrwitek/utility-types on GitHub
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/piotrwitek-utility-types.svg)](https://hysenlabs.com/projects/piotrwitek-utility-types)