Hone logo
Hone
Problems

Utility Types: Pick and Omit in TypeScript

TypeScript's utility types Pick and Omit are powerful tools for manipulating existing types. This challenge asks you to implement these utility types from scratch, deepening your understanding of TypeScript's type system. Implementing these utilities will allow you to create more concise and maintainable code by selectively extracting or excluding properties from complex types.

Problem Description

You are tasked with creating two TypeScript functions, pick and omit, that mimic the behavior of the built-in Pick and Omit utility types. These functions should take a type and a set of keys as input and return a new type containing only the specified keys (for pick) or all keys except the specified ones (for omit).

What needs to be achieved:

  • Implement a function pick<T, K extends keyof T>(obj: T, keys: K[]): Pick<T, K> that takes a type T and an array of keys K (where K is a subset of the keys of T) and returns a new type with only the properties specified in K.
  • Implement a function omit<T, K extends keyof T>(obj: T, keys: K[]): Omit<T, K> that takes a type T and an array of keys K (where K is a subset of the keys of T) and returns a new type with all properties of T except those specified in K.

Key Requirements:

  • The functions must be type-safe. The TypeScript compiler should be able to infer the correct return type based on the input types.
  • The functions should work with any valid TypeScript type T.
  • The keys argument must be an array of keys that are valid keys of the type T.
  • The functions should not modify the original type T. They should return a new type.

Expected Behavior:

The functions should return a new type that accurately reflects the selected or omitted properties. The type inference should be correct, allowing you to use the resulting type in other parts of your code.

Edge Cases to Consider:

  • Empty keys array: pick should return the original type T, and omit should also return the original type T.
  • keys array containing keys that don't exist on T: The TypeScript compiler should flag this as an error.
  • T is a primitive type (e.g., string, number, boolean): Both functions should return T as primitive types cannot have properties picked or omitted.
  • T is a union type: The functions should handle union types correctly, preserving the union.

Examples

Example 1: pick

type MyType = {
  name: string;
  age: number;
  city: string;
};

type NameAndCity = pick<MyType, ['name', 'city']>;

// NameAndCity will be: { name: string; city: string; }

Explanation: We pick the 'name' and 'city' properties from MyType, resulting in a new type with only those properties.

Example 2: omit

type MyType = {
  name: string;
  age: number;
  city: string;
};

type NameAndAge = omit<MyType, ['city']>;

// NameAndAge will be: { name: string; age: number; }

Explanation: We omit the 'city' property from MyType, resulting in a new type with only the 'name' and 'age' properties.

Example 3: Empty keys array

type MyType = {
  name: string;
  age: number;
  city: string;
};

type AllProperties = pick<MyType, []>;
type NoProperties = omit<MyType, []>;

// AllProperties will be: { name: string; age: number; city: string; }
// NoProperties will be: { name: string; age: number; city: string; }

Explanation: When the keys array is empty, both pick and omit return the original type.

Constraints

  • Type Safety: The solution must be type-safe and leverage TypeScript's type system effectively.
  • Input Type: The obj parameter should be of any valid TypeScript type.
  • Key Type: The keys parameter should be an array of strings representing the keys to pick or omit.
  • Performance: While performance is not a primary concern, avoid unnecessarily complex or inefficient logic. The focus is on correctness and type safety.
  • No Built-in Utility Types: You cannot use the built-in Pick or Omit utility types in your implementation. The goal is to implement them yourself.

Notes

  • Consider using conditional types and mapped types to achieve the desired behavior.
  • Think about how to handle the case where the input type is a primitive.
  • Pay close attention to type inference to ensure your functions are working correctly.
  • This challenge is designed to test your understanding of advanced TypeScript type manipulations. Don't be afraid to experiment and explore different approaches.
Loading editor...
typescript