Hone logo
Hone
Problems

TypeScript Export Type Helpers

This challenge focuses on creating robust and flexible type utility functions in TypeScript to manage and re-export types from other modules. This is a common practice in library development to provide a clean and organized API for consumers, allowing them to import only what's necessary from your package without exposing internal implementation details.

Problem Description

Your task is to implement a set of TypeScript type helper functions that facilitate the export of types from a module. These helpers should enable users of your module to import specific types, a union of types, or all exported types from a source module.

Key Requirements:

  1. ExportType<T, K>: A type that takes a source type T (typically an object type or a module namespace object) and a union of keys K (strings representing property names). It should produce a new type containing only the properties of T whose keys are present in K.

  2. ExportUnion<T, K>: A type that, given a source type T and a union of keys K, produces a union of the types of the properties of T whose keys are present in K.

  3. ExportAll<T>: A type that takes a source type T and effectively creates a new type that is an exact replica of T. This is useful for re-exporting an entire module's types.

Expected Behavior:

  • The helper types should work correctly with various types of properties (primitives, objects, arrays, functions, unions, intersections, etc.).
  • The keys K provided to ExportType and ExportUnion should be treated as a union of string literals.
  • The helper types should be composable and usable within other TypeScript type definitions.

Edge Cases to Consider:

  • What happens if a key specified in K does not exist in T? (The types should gracefully handle this by not including non-existent keys).
  • What happens if T is an empty object type?
  • What happens if K is an empty union?

Examples

Example 1: ExportType

Let's assume we have a module src/data.ts with the following types:

// src/data.ts
export interface User {
  id: number;
  name: string;
  email: string;
  isActive: boolean;
}

export interface Product {
  id: number;
  name: string;
  price: number;
  description: string;
}

Now, we want to create a type that only exports the id and name from User and Product.

// In your library's API definition file (e.g., src/api.ts)

// Assume 'data' is imported from 'src/data'
// import * as data from './data';

type SourceTypes = typeof data; // This would represent { User: typeof User, Product: typeof Product }

// We want to export specific fields from the User type
type ExportedUserFields = ExportType<typeof data.User, 'id' | 'name'>;
// Expected: { id: number; name: string; }

// We want to export specific fields from the Product type
type ExportedProductFields = ExportType<typeof data.Product, 'id' | 'price'>;
// Expected: { id: number; price: number; }

Example 2: ExportUnion

Using the same src/data.ts as above.

// In your library's API definition file (e.g., src/api.ts)

// Assume 'data' is imported from 'src/data'
// import * as data from './data';

// We want to export the types of 'id' from both User and Product
type ExportedIds = ExportUnion<typeof data.User, 'id'> | ExportUnion<typeof data.Product, 'id'>;
// Expected: number | number (which simplifies to number)

// We want to export the types of 'name' from both User and Product
type ExportedNames = ExportUnion<typeof data.User, 'name'> | ExportUnion<typeof data.Product, 'name'>;
// Expected: string | string (which simplifies to string)

// Exporting a union of different types
type MixedExports = ExportUnion<typeof data.User, 'id' | 'isActive'> | ExportUnion<typeof data.Product, 'name' | 'price'>;
// Expected: number | boolean | string | number

Example 3: ExportAll

If src/data.ts is intended to be fully re-exported.

// src/api.ts

// import * as data from './data';

// Re-exporting all types from data
type AllDataTypes = ExportAll<typeof data>;
// Expected: { User: typeof User, Product: typeof Product }
// (Where typeof User and typeof Product are the actual types themselves)

Constraints

  • Your helper types must be implemented purely using TypeScript's built-in utility types and features. No runtime code is allowed.
  • The solution should aim for clarity and maintainability.
  • The types should be efficient for the TypeScript compiler to process.

Notes

  • Consider how keyof and indexed access types can be leveraged.
  • Think about how to handle conditional types for selecting specific properties.
  • For ExportAll, you might need to think about mapping types.
  • The typeof operator will be crucial for getting the type of your imported modules or objects.
  • The goal is to provide a type-level API, not to generate JavaScript code.
Loading editor...
typescript