31824-hard-length-of-string-3

Back

type ToInt<S> = S extends `0${infer X}` ? ToInt<X> : S extends `${infer N extends number}` ? N : 0;

type Q1 = string;
type Q10 = `${Q1}${Q1}${Q1}${Q1}${Q1}${Q1}${Q1}${Q1}${Q1}${Q1 & {}}`;
type Q100 = `${Q10}${Q10}${Q10}${Q10}${Q10}${Q10}${Q10}${Q10}${Q10}${Q10}`;
type Q1000 = `${Q100}${Q100}${Q100}${Q100}${Q100}${Q100}${Q100}${Q100}${Q100}${Q100}`;
type Q10k = `${Q1000}${Q1000}${Q1000}${Q1000}${Q1000}${Q1000}${Q1000}${Q1000}${Q1000}${Q1000}`;
type Q100k = `${Q10k}${Q10k}${Q10k}${Q10k}${Q10k}${Q10k}${Q10k}${Q10k}${Q10k}${Q10k}`;

type Len<S, Q extends string, R extends 1[] = []> = S extends `${Q}${infer T}`
  ? Len<T, Q, [...R, 1]>
  : [R['length'], S];

type LengthOfString<S extends string> = Len<S, Q100k> extends [infer A extends number, infer S1]
  ? Len<S1, Q10k> extends [infer B extends number, infer S2]
    ? Len<S2, Q1000> extends [infer C extends number, infer S3]
      ? Len<S3, Q100> extends [infer D extends number, infer S4]
        ? Len<S4, Q10> extends [infer E extends number, infer S5]
          ? Len<S5, Q1> extends [infer F extends number, string]
            ? ToInt<`${A}${B}${C}${D}${E}${F}`>
            : 0
          : 0
        : 0
      : 0
    : 0
  : 0;

Play

Solution by alexandroppolus #33269

Thoughts

I prefer not to hardcode a specific length for the assessment, as the logic should theoretically handle strings of any length. However, in practice, excessively long strings can cause browser issues. Despite this limitation, I aim for the logic to support strings of any length.

Approach

While many prefer concise solutions, my focus is on addressing edge cases and optimizing performance to ensure robustness and scalability, even if it requires a more detailed implementation.

// Performance Note:
// The nested structure of these types is designed for efficiency. By reducing the size of S incrementally and
// processing it in chunks (one `L` at a time), we minimize the amount of data being processed in each recursive step.
// This improves performance and avoids working with excessively long strings, which could slow down type evaluation.

// Entry point: Calculate the length of string S by recursively breaking it into parts
type LengthOfString<S extends string, L extends string = `${any}`, R extends string = ''> = S extends `${L}${infer S}` ? Remainder1<S, L, R> : DigitAccumulator<S, R, '0'>

// Recursive helper: Used after the initial 10 levels of recursion, processes the remaining length of S
type Remainder1<S extends string, L extends string, R extends string> = S extends `${L}${infer S}` ? Remainder2<S, L, R> : DigitAccumulator<S, R, '1'>
type Remainder2<S extends string, L extends string, R extends string> = S extends `${L}${infer S}` ? Remainder3<S, L, R> : DigitAccumulator<S, R, '2'>
type Remainder3<S extends string, L extends string, R extends string> = S extends `${L}${infer S}` ? Remainder4<S, L, R> : DigitAccumulator<S, R, '3'>
type Remainder4<S extends string, L extends string, R extends string> = S extends `${L}${infer S}` ? Remainder5<S, L, R> : DigitAccumulator<S, R, '4'>
type Remainder5<S extends string, L extends string, R extends string> = S extends `${L}${infer S}` ? Remainder6<S, L, R> : DigitAccumulator<S, R, '5'>
type Remainder6<S extends string, L extends string, R extends string> = S extends `${L}${infer S}` ? Remainder7<S, L, R> : DigitAccumulator<S, R, '6'>
type Remainder7<S extends string, L extends string, R extends string> = S extends `${L}${infer S}` ? Remainder8<S, L, R> : DigitAccumulator<S, R, '7'>
type Remainder8<S extends string, L extends string, R extends string> = S extends `${L}${infer S}` ? Remainder9<S, L, R> : DigitAccumulator<S, R, '8'>
// Continue breaking down the string into chunks of L until all parts are processed
type Remainder9<S extends string, L extends string, R extends string> = S extends `${L}${infer S}` ? Remainder1<S, `${L}${L}${L}${L}${L}${L}${L}${L}${L}${L}`, `${L}.${R}`> : DigitAccumulator<S, R, '9'>

// Final helper: Accumulates the count of digits for the remaining parts of S
type DigitAccumulator<S extends string, R extends string, A extends string = ''> = R extends `${infer L}.${infer R}`
  ? Digit0<S, L, R, A> : A extends `${infer N extends number}` ? N : never
type Digit0<S extends string, L extends string, R extends string, A extends string = ''> = S extends `${L}${infer S}` ? Digit1<S, L, R, A> : DigitAccumulator<S, R, `${A}0`>
type Digit1<S extends string, L extends string, R extends string, A extends string = ''> = S extends `${L}${infer S}` ? Digit2<S, L, R, A> : DigitAccumulator<S, R, `${A}1`>
type Digit2<S extends string, L extends string, R extends string, A extends string = ''> = S extends `${L}${infer S}` ? Digit3<S, L, R, A> : DigitAccumulator<S, R, `${A}2`>
type Digit3<S extends string, L extends string, R extends string, A extends string = ''> = S extends `${L}${infer S}` ? Digit4<S, L, R, A> : DigitAccumulator<S, R, `${A}3`>
type Digit4<S extends string, L extends string, R extends string, A extends string = ''> = S extends `${L}${infer S}` ? Digit5<S, L, R, A> : DigitAccumulator<S, R, `${A}4`>
type Digit5<S extends string, L extends string, R extends string, A extends string = ''> = S extends `${L}${infer S}` ? Digit6<S, L, R, A> : DigitAccumulator<S, R, `${A}5`>
type Digit6<S extends string, L extends string, R extends string, A extends string = ''> = S extends `${L}${infer S}` ? Digit7<S, L, R, A> : DigitAccumulator<S, R, `${A}6`>
type Digit7<S extends string, L extends string, R extends string, A extends string = ''> = S extends `${L}${infer S}` ? Digit8<S, L, R, A> : DigitAccumulator<S, R, `${A}7`>
// Reduce string S by one `L` at a time to minimize its size and improve performance
type Digit8<S extends string, L extends string, R extends string, A extends string = ''> = S extends `${L}${infer S}` ? DigitAccumulator<S, R, `${A}9`> : DigitAccumulator<S, R, `${A}8`>

Playground

Solution by teamchong #33139