Hone logo
Hone
Problems

Dynamically Extending Interfaces with Conditional Logic in TypeScript

This challenge focuses on a powerful TypeScript feature: conditional types. You'll learn how to create generic types that can extend one interface or another based on a condition related to the type parameter. This is incredibly useful for building flexible and type-safe libraries or frameworks where the shape of an object might depend on configuration or other type-level information.

Problem Description

Your task is to implement a generic TypeScript type named ConditionalExtend. This type should accept two type parameters:

  1. Base: The primary type to be extended.
  2. Condition: A type that will be checked.

The ConditionalExtend type should behave as follows:

  • If Condition is assignable to string, ConditionalExtend<Base, Condition> should extend Base and an additional property label of type string.
  • If Condition is not assignable to string (i.e., it's any other primitive type or object), ConditionalExtend<Base, Condition> should extend Base and an additional property value of type number.

You need to define this ConditionalExtend type using TypeScript's conditional type syntax.

Examples

Example 1:

type MyBaseType = { id: number };

// Condition is string
type Result1 = ConditionalExtend<MyBaseType, "active">;

// Expected type inference for Result1:
// {
//   id: number;
//   label: string;
// }

Explanation: Since "active" is assignable to string, ConditionalExtend adds the label: string property to MyBaseType.

Example 2:

type AnotherBaseType = { name: string };

// Condition is number
type Result2 = ConditionalExtend<AnotherBaseType, 123>;

// Expected type inference for Result2:
// {
//   name: string;
//   value: number;
// }

Explanation: Since 123 is not assignable to string, ConditionalExtend adds the value: number property to AnotherBaseType.

Example 3:

type YetAnotherBaseType = { timestamp: Date };

// Condition is boolean
type Result3 = ConditionalExtend<YetAnotherBaseType, true>;

// Expected type inference for Result3:
// {
//   timestamp: Date;
//   value: number;
// }

Explanation: Since true is not assignable to string, ConditionalExtend adds the value: number property to YetAnotherBaseType.

Example 4:

type ConfigType = { settings: string[] };

// Condition is a union type that *includes* string
type Result4 = ConditionalExtend<ConfigType, "config" | number>;

// Expected type inference for Result4:
// {
//   settings: string[];
//   label: string;
// }

Explanation: The union type "config" | number is assignable to string because "config" is assignable to string. Therefore, the label: string property is added.

Constraints

  • The solution must be a single generic type alias named ConditionalExtend.
  • The type must correctly implement the conditional logic as described.
  • No runtime code is required; this is a purely type-level challenge.

Notes

  • Focus on the syntax for conditional types in TypeScript (T extends U ? X : Y).
  • Pay close attention to how TypeScript resolves assignability for primitive types and unions.
  • Consider using an intersection (&) to combine the Base type with the newly added property.
Loading editor...
typescript