npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

ts-subset

v0.0.1

Published

A TypeScript utility inspired by GraphQL's Fragment, for creating type-safe subsets of object types

Readme

ts-subset

npm version License: MIT

A TypeScript utility for creating type-safe subsets of objects. Inspired by GraphQL's Fragment, this library allows you to deeply extract specific fields from complex object types.

Why ts-subset?

Working with deeply nested objects can be overwhelming. Whether you're dealing with APIs, large data structures, or complex schemas, selecting only the necessary fields while preserving TypeScript type safety can simplify your code and reduce runtime errors.

With ts-subset, you can:

  • Select specific fields, even from deeply nested structures.
  • Use reusable "fragments" to define and embed pre-defined selections in other types.
  • Select the entire object with true when needed.
  • Preserve optional and nullable fields in the resulting type.
  • Safely narrow down types to what you actually need.

##⠀Installation

npm install ts-subset

Usage

Here's how to use Subset<T, S> to extract specific fields from your types:

import { Subset } from "ts-subset";

// Example object types
type Obj = {
  id: number;
  nested: Nested;
  nestedOpt?: Nested | null;
  items: Nested[];
};
type Nested = {
  id: number;
  details: {
    name: string;
    age?: number | null;
  };
};

// Define the subset selection
type ObjFragment = Subset<Obj, {
  id: true;
  nested: {
    details: {
      name: true;
    };
  };
  nestedOpt: {
    details: {
      name: true;
    };
  };
  items: {
    details: {
      name: true;
    };
  };
}>;
// The resulting type:
// {
//   id: number;
//   nested: { details: { name: string } };
//   nestedOpt?: { details: { name: string } } | null;
//   items: { details: { name: string } }[];
// }

Features

Select Entire Objects

When true is specified for an object field, the entire object is selected:

type Obj = {
  id: number;
  details: {
    name: string;
    age?: number | null;
  };
};

type ObjFragment = Subset<Obj, {
  details: true;
}>;
// Result:
// {
//   details: { name: string; age?: number | null };
// }

Select Optional and Nullable Fields

The library preserves the optional (?) and nullable (| null, | undefined) properties of your types:

type Obj = {
  id: number;
  optionalField?: string;
  nullableField: string | null;
};

type ObjFragment = Subset<Obj, {
  id: true;
  optionalField: true;
  nullableField: true;
}>;
// Result:
// {
//   id: number;
//   optionalField?: string;
//   nullableField: string | null;
// }

Handle Arrays

Extract specific fields from arrays while keeping their structure:

type Obj = {
  items: { id: number; value: string }[];
};

type ObjFragment = Subset<Obj, {
  items: { id: true };
}>;
// Result:
// {
//   items: { id: number }[];
// }

Work with Deeply Nested Structures

Select fields from deeply nested objects:

type Obj = {
  nested: {
    level1: {
      level2: {
        field: string;
        other: string;
      };
      other: string;
    };
  };
};

type ObjFragment = Subset<Obj, {
  nested: {
    level1: {
      level2: {
        field: true;
      };
    };
  };
}>;
// Result:
// {
//   nested: {
//     level1: {
//       level2: {
//         field: string;
//       };
//     };
//   };
// }

Use Embedded Fragments (Reusable Selections)

You can define reusable "fragments" (pre-defined selections) and embed them into other types using a tuple notation [Type].

type Obj = {
  id: number;
  nested: Nested;
  nestedOpt?: Nested | null;
};
type Nested = {
  id: number;
  details: {
    name: string;
    age?: number | null;
  };
};

// Define a reusable fragment
type NestedFragment = Subset<Nested, {
  id: true;
}>;

// Use the fragment in another selection
type ObjFragment = Subset<Obj, {
  id: true;
  nested: [NestedFragment]; // Embed the fragment
  nestedOpt: [NestedFragment]; // Embed the fragment
}>;
// Result:
// {
//   id: number;
//   nested: { id: number };
//   nested?: { id: number } | null;
// }

Note: The embedded type is not validated against the original field's type. See the limitations section for more details.


Limitations

While ts-subset is powerful, it has some limitations:

  • Embedding fragments requires manual validation: When embedding pre-defined fragments using [Type], the embedded type is not validated against the field's original type. For example:
    type Obj = { field: { id: number } };
    
    type OtherFragment = { invalid: string }; // Doesn't match `Obj["field"]` type
    
    type Error = Subset<Obj, { field: [OtherFragment] }>; // No compile-time error
    // Result:
    // { field: { invalid: string } }
    This is a limitation of TypeScript's type system and cannot be enforced. Users must ensure the embedded fragment is compatible with the target field.
  • Cannot transform values: This library is purely for type refinement. It does not provide runtime transformations.
  • No support for conditional or computed fields: Types like Record<string, any> or mapped types ({ [key: K]: T }) are not supported for selection.
  • No runtime validation: ts-subset operates entirely at the type level. It does not enforce selections or transformations at runtime.

License

MIT