Hone logo
Hone
Problems

Advanced Intersection Type Utilities in TypeScript

TypeScript's intersection types are powerful, but sometimes you need more sophisticated ways to work with them. This challenge asks you to create utility types that manipulate intersection types, specifically focusing on extracting common properties and creating new types based on intersection properties. This is useful for creating more type-safe and maintainable code when dealing with complex object structures.

Problem Description

You are tasked with creating three TypeScript utility types: CommonProperties, PickIntersection, and OmitIntersection.

  • CommonProperties<T, U>: This utility type should take two types, T and U, and return a type that represents only the properties that exist in both T and U. It should preserve the original types of those common properties. Essentially, it finds the intersection of the keys of T and U.

  • PickIntersection<T, U, K>: This utility type should take two types, T and U, and a union of keys K. It should return a type that picks only the properties from K that exist in the intersection of T and U. If a key in K doesn't exist in both T and U, it should be omitted from the resulting type.

  • OmitIntersection<T, U, K>: This utility type should take two types, T and U, and a union of keys K. It should return a type that omits the properties from the intersection of T and U that are present in K. In other words, it takes the intersection of T and U and then removes any properties specified in K.

Examples

Example 1: CommonProperties

type T = { a: string; b: number; c: boolean };
type U = { b: string; c: number; d: symbol };

type Common = CommonProperties<T, U>;
// Expected Output: { b: number; c: boolean }

Explanation: Both T and U have properties b and c. b has type number in T and string in U. The intersection type resolves to the most restrictive type, which is number. c has type boolean in T and number in U. The intersection type resolves to the most restrictive type, which is boolean.

Example 2: PickIntersection

type T = { a: string; b: number; c: boolean };
type U = { b: string; c: number; d: symbol };
type Keys = 'a' | 'b' | 'c' | 'd' | 'e';

type Picked = PickIntersection<T, U, Keys>;
// Expected Output: { b: number; c: boolean }

Explanation: The intersection of T and U is { b: number; c: boolean }. Keys includes 'a', 'b', 'c', 'd', and 'e'. Only 'b' and 'c' exist in both T and U, so they are picked.

Example 3: OmitIntersection

type T = { a: string; b: number; c: boolean };
type U = { b: string; c: number; d: symbol };
type Keys = 'b' | 'd' | 'e';

type Omitted = OmitIntersection<T, U, Keys>;
// Expected Output: { c: boolean }

Explanation: The intersection of T and U is { b: number; c: boolean }. Keys includes 'b', 'd', and 'e'. 'b' is in the intersection, so it's omitted. 'd' and 'e' are not in the intersection, so they have no effect. The result is { c: boolean }.

Constraints

  • All utility types must be valid TypeScript types.
  • The types should handle cases where there are no common properties gracefully (returning an empty object type {}).
  • The types should correctly handle properties with conflicting types in T and U (using the most restrictive type).
  • The utility types should be performant enough for typical use cases. Avoid unnecessary complexity.
  • The Keys type in PickIntersection and OmitIntersection must be a union of string literals.

Notes

  • Consider using conditional types and mapped types to achieve the desired behavior.
  • Think about how to handle type narrowing to ensure accurate results.
  • Pay close attention to how TypeScript resolves intersection types with conflicting property types.
  • Testing with various combinations of types and keys is crucial to ensure correctness.
  • The goal is to create reusable and type-safe utility types that can be used in various scenarios.
Loading editor...
typescript