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:
Base: The primary type to be extended.Condition: A type that will be checked.
The ConditionalExtend type should behave as follows:
- If
Conditionis assignable tostring,ConditionalExtend<Base, Condition>should extendBaseand an additional propertylabelof typestring. - If
Conditionis not assignable tostring(i.e., it's any other primitive type or object),ConditionalExtend<Base, Condition>should extendBaseand an additional propertyvalueof typenumber.
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 theBasetype with the newly added property.