Hone logo
Hone
Problems

Crafting a Utility Types Toolkit with keyof in TypeScript

This challenge focuses on leveraging TypeScript's powerful keyof operator to create a suite of reusable utility types. Building these utilities demonstrates a deep understanding of type manipulation and allows for more concise and type-safe code across projects. You'll be crafting types that extract, transform, and filter keys from existing types.

Problem Description

Your task is to implement a set of utility types using TypeScript's keyof operator and other type manipulation tools. These utilities will operate on existing types, extracting and transforming their keys. The goal is to create a modular and reusable set of type definitions that can be used to enhance type safety and code clarity.

Specifically, you need to implement the following utility types:

  1. PickKeys<T, K>: This type should take a type T and a union of keys K (e.g., keyof T). It should return a new type containing only the properties of T whose keys are included in the K union.

  2. OmitKeys<T, K>: This type should take a type T and a union of keys K (e.g., keyof T). It should return a new type containing all properties of T except those whose keys are included in the K union.

  3. ValueOfKeys<T>: This type should take a type T and return a type consisting of the values of the properties whose keys are present in T. Essentially, it extracts the value types from a type based on its keys.

  4. RequiredKeys<T>: This type should take a type T and return a new type where all properties are required, even those that might be optional in the original type.

  5. PartialKeys<T, K>: This type should take a type T and a union of keys K (e.g., keyof T). It should return a new type where only the properties specified by K are partial, and all other properties remain as they were in the original type T.

Examples

Example 1: PickKeys

type MyType = {
  name: string;
  age: number;
  isActive: boolean;
};

type KeysToPick = keyof MyType; // "name" | "age" | "isActive"

type PickedType = PickKeys<MyType, KeysToPick>;
// PickedType should be equivalent to MyType: { name: string; age: number; isActive: boolean; }

Explanation: We pick all keys, so the result is the original type.

Example 2: OmitKeys

type MyType = {
  name: string;
  age: number;
  isActive: boolean;
};

type KeysToOmit = "age" | "isActive";

type OmittedType = OmitKeys<MyType, KeysToOmit>;
// OmittedType should be: { name: string; }

Explanation: We omit "age" and "isActive", leaving only "name".

Example 3: ValueOfKeys

type MyType = {
  name: string;
  age: number;
  isActive: boolean;
};

type ValueType = ValueOfKeys<MyType>;
// ValueType should be: string | number | boolean

Explanation: We extract the value types associated with each key.

Example 4: RequiredKeys

type MyType = {
  name?: string;
  age: number;
  isActive?: boolean;
};

type RequiredType = RequiredKeys<MyType>;
// RequiredType should be: { name: string; age: number; isActive: boolean; }

Explanation: All properties are now required.

Example 5: PartialKeys

type MyType = {
  name: string;
  age: number;
  isActive: boolean;
};

type KeysToPartial = "name" | "age";

type PartialedType = PartialKeys<MyType, KeysToPartial>;
// PartialedType should be: {
//   name?: string;
//   age?: number;
//   isActive: boolean;
// }

Explanation: Only "name" and "age" are now optional.

Constraints

  • All utility types must be implemented using TypeScript's type system features, including keyof, conditional types, mapped types, and utility types like Pick, Omit, and Required.
  • The code should be well-formatted and readable.
  • The utility types should be generic and work with any valid TypeScript type.
  • No runtime code is allowed; this is purely a type-level challenge.
  • The solution should be self-contained and not rely on external libraries.

Notes

  • Consider using mapped types ([T in Keys] => ...) to iterate over the keys of a type.
  • Conditional types (T extends U ? X : Y) can be helpful for handling different scenarios based on the type of a key.
  • The Pick and Omit utility types can be used as building blocks, but you should demonstrate understanding of how keyof works in conjunction with them.
  • Think about how to handle optional properties correctly in OmitKeys and RequiredKeys.
  • Focus on type safety and ensuring that the utility types produce the expected results for various input types. Test your types thoroughly with different scenarios.
Loading editor...
typescript