Behold the mischievous 😂 (with some clarifications in code annotations):
// Select only string representations of positive numbers
type Positive<N> = N extends `${number}` ? N extends "0" ? never : N : never
// Generate a union of possible array indexes (starting from 1)
type IndexFrom1<T extends Array<any>> = keyof { [K in keyof [null, ...T] as Positive<K>]: any }
type WithPrefix<T extends Array<any>, Prefix extends string, Value = IndexFrom1<T>> =
Value extends string ? `${Prefix}-${Value}` : never
type N_Test = IndexFrom1<[string, string, string]> // "1" | "2" | "3"
type P_Test = WithPrefix<[string, string, string], 'Test'> // "Test-1" | "Test-2" | "Test-3"
Now let's apply the WithPrefix
function to your scenarios:
type DefaultTheme = {
name: 'default';
colors: [string, string, string];
};
type CustomTheme = {
name: 'custom';
colors: [string, string, string, string, string];
};
type DefaultColor = WithPrefix<DefaultTheme["colors"], DefaultTheme["name"]>; // 'default-1' | 'default-2' | 'default-3';
type CustomColor = WithPrefix<CustomTheme["colors"], CustomTheme["name"]>; // 'custom-1' | 'custom-2' | 'custom-3' | 'custom-4' | 'custom-5';
Interactive Workspace
Initially, we create a union of tuple indexes by utilizing mapped typing. We introduce an additional element to it ([null, ...T]
) and exclude the zero index (N extends "0"
) to commence from 1
.
Subsequently, we employ a distributive conditional type with template literal type to generate the expected union.
Value extends string ? `${Prefix}-${Value}` : never