Hone logo
Hone
Problems

Advanced Type Narrowing Utilities in TypeScript

TypeScript's type system is powerful, but sometimes you need more sophisticated ways to narrow down types based on complex conditions. This challenge asks you to create reusable type narrowing utility functions that go beyond the standard typeof, instanceof, and discriminated unions. Building these utilities will deepen your understanding of TypeScript's type system and allow you to write more robust and maintainable code.

Problem Description

You are tasked with creating three TypeScript utility types that perform advanced type narrowing. These utilities should take a type as input and return a narrowed type based on specific criteria.

  1. IsStringLike(T): This utility should take a type T and return a type that represents T if it's a string or a type that can be reasonably treated as a string (e.g., string[], number which can be converted to a string). If T is not string-like, it should return never.

  2. ExtractNumericProperties<T>: This utility should take a type T (an object type) and return a type consisting only of the properties of T whose values are numbers. The keys of the resulting type should be the same as the keys of the original type. If T is not an object type, return never.

  3. FilterByType<T, U>: This utility should take two types, T (an object type) and U (a type constructor, like string or number). It should return a type that consists of only the properties of T whose values are assignable to U. The keys of the resulting type should be the same as the keys of the original type. If T is not an object type, return never.

Examples

Example 1: IsStringLike

Input: string
Output: string
Explanation: `string` is a string, so it's string-like.

Input: number
Output: never
Explanation: `number` is not string-like.

Input: string[]
Output: string[]
Explanation: `string[]` can be reasonably treated as a string.

Input: { name: string, age: number }
Output: never
Explanation: An object is not string-like.

Example 2: ExtractNumericProperties

Input: { name: string, age: number, count: number }
Output: { age: number, count: number }
Explanation: Only `age` and `count` are numeric properties.

Input: { name: string, age: string }
Output: {}
Explanation: No properties are numeric.

Input: number
Output: never
Explanation: `number` is not an object type.

Example 3: FilterByType

Input: T = { name: string, age: number, count: number }, U = string
Output: { name: string }
Explanation: Only `name` is assignable to `string`.

Input: T = { name: string, age: number, count: number }, U = number
Output: { age: number, count: number }
Explanation: Only `age` and `count` are assignable to `number`.

Input: T = { name: string, age: number, count: number }, U = boolean
Output: {}
Explanation: No properties are assignable to `boolean`.

Input: T = number, U = string
Output: never
Explanation: `number` is not an object type.

Constraints

  • All utility types must be implemented using TypeScript's type system features (conditional types, mapped types, etc.).
  • The utilities should be as generic as possible to handle a wide range of input types.
  • The code should be well-formatted and easy to understand.
  • The solutions should be type-safe and avoid any runtime errors.

Notes

  • Consider using conditional types to check the type of the input.
  • Mapped types can be helpful for iterating over properties of an object type.
  • typeof and keyof operators can be useful for extracting information about types.
  • Think about how to handle edge cases, such as when the input type is never or any.
  • For IsStringLike, consider what types you would reasonably want to treat as strings. Don't try to be exhaustive, but cover common cases.
  • For ExtractNumericProperties and FilterByType, remember that the keys of the output type should match the keys of the input type.
Loading editor...
typescript