Hone logo
Hone
Problems

Conditional Type Helpers in TypeScript

TypeScript's conditional types are a powerful feature allowing you to create types that depend on other types. This challenge focuses on implementing several common conditional type helpers, enabling you to manipulate and refine types based on conditions. Successfully completing this challenge demonstrates a strong understanding of advanced TypeScript type system concepts.

Problem Description

You are tasked with implementing several utility conditional types in TypeScript. These types will take existing types as input and return a new type based on a condition evaluated against those input types. Specifically, you need to implement the following:

  1. IsNever<T>: This type should resolve to true if T is never, and false otherwise.
  2. Extract<T, U>: This type should extract and construct a type consisting of all types in T that also satisfy U. Essentially, it filters types within a union.
  3. Omit<T, K>: This type should exclude certain keys from a type T. K is a union of keys to exclude.
  4. PartialBy<T, K>: This type should make certain properties of T optional. K is a union of keys to make optional.
  5. Pick<T, K>: This type should pick certain keys from a type T. K is a union of keys to pick.

These helpers are fundamental building blocks for more complex type manipulations and are frequently used in libraries and frameworks.

Examples

Example 1: IsNever<T>

Input: T = never
Output: true

Input: T = string
Output: false

Explanation: IsNever<never> resolves to true because never is the only type that satisfies the condition of being never. IsNever<string> resolves to false because string is not never.

Example 2: Extract<T, U>

Input: T = string | number | boolean, U = string
Output: string

Explanation: Extract<string | number | boolean, string> filters the union string | number | boolean and returns only the types that are assignable to string, which is just string.

Example 3: Omit<T, K>

Input: T = { a: string; b: number; c: boolean }, K = 'b'
Output: { a: string; c: boolean }

Explanation: Omit<{ a: string; b: number; c: boolean }, 'b' removes the property b from the type, resulting in { a: string; c: boolean }.

Example 4: PartialBy<T, K>

Input: T = { a: string; b: number; c: boolean }, K = 'b'
Output: { a: string; b?: number; c: boolean }

Explanation: PartialBy<{ a: string; b: number; c: boolean }, 'b' makes the property b optional, resulting in { a: string; b?: number; c: boolean }.

Example 5: Pick<T, K>

Input: T = { a: string; b: number; c: boolean }, K = 'a' | 'c'
Output: { a: string; c: boolean }

Explanation: Pick<{ a: string; b: number; c: boolean }, 'a' | 'c' picks the properties a and c from the type, resulting in { a: string; c: boolean }.

Constraints

  • All implemented types must be valid TypeScript conditional types.
  • The implementations should be as concise and efficient as possible, leveraging TypeScript's type system effectively.
  • The types should handle various input types correctly, including primitive types, unions, intersections, and object types.
  • The code should be well-formatted and readable.

Notes

  • Consider using the infer keyword within conditional types to extract type information.
  • Think about how to handle edge cases, such as when K is an empty union in Omit, PartialBy, and Pick.
  • These types are intended to be pure type-level operations; they should not involve any runtime code.
  • Focus on the type definitions themselves; no runtime tests are required. The correctness of your solution will be evaluated by TypeScript's type checker.
Loading editor...
typescript