koSakano/type-challenges

925 - Assert Array Index

Opened this issue · 0 comments

type Num = ReadonlyArray<0>;
type N0 = readonly [];
type N1 = readonly [0];
type N2 = readonly [0, 0];
type N3 = readonly [0, 0, 0];
type N4 = readonly [0, 0, 0, 0];
type N5 = readonly [0, 0, 0, 0, 0];
type N6 = readonly [0, 0, 0, 0, 0, 0];
type N7 = readonly [0, 0, 0, 0, 0, 0, 0];
type N8 = readonly [0, 0, 0, 0, 0, 0, 0, 0];
type N9 = readonly [0, 0, 0, 0, 0, 0, 0, 0, 0];

/**
 * Sum<N3, N4> = N7.
 */
type Sum<N extends Num, M extends Num> = readonly [...N, ...M];

type NA = Sum<N9, N7>;
type NI = Sum<NA, Sum<N9, N5>>;
type NP = Sum<NI, Sum<N9, N8>>;
type NX = Sum<NP, Sum<N9, N4>>;

type Codes = {
    ' ': N7;
    a: Sum<NA, N1>;
    b: Sum<NA, N2>;
    c: Sum<NA, N3>;
    d: Sum<NA, N4>;
    e: Sum<NA, N5>;
    f: Sum<NA, N6>;
    g: Sum<NA, N7>;
    h: Sum<NA, N8>;
    i: Sum<NI, N1>;
    j: Sum<NI, N2>;
    k: Sum<NI, N3>;
    l: Sum<NI, N4>;
    m: Sum<NI, N5>;
    n: Sum<NI, N6>;
    o: Sum<NI, N7>;
    p: Sum<NP, N1>;
    q: Sum<NP, N2>;
    r: Sum<NP, N3>;
    s: Sum<NP, N4>;
    t: Sum<NP, N5>;
    u: Sum<NP, N6>;
    v: Sum<NP, N7>;
    w: Sum<NP, N9>;
    x: Sum<NX, N1>;
    y: Sum<NX, N2>;
    z: Sum<NX, N3>;
};

/**
 * KeyToNum<'ab'> = N74.
 */
type KeyToNum<Key extends string> = Key extends ''
    ? N0
    : Key extends `${infer L}${infer Rest}`
    ? L extends keyof Codes
        ? Sum<Codes[L], KeyToNum<Rest>>
        : never
    : never;

/**
 * IsArray<[0]> = false, IsArray<string[]> = true.
 */
type IsArray<A extends readonly unknown[]> = number extends A['length'] ? true : false;

/**
 * IsKey<'ab x'> = true, IsKey<'key!'> = false.
 */
type IsKey<Key extends string> = Key extends ''
    ? false
    : KeyToNum<Key> extends never
    ? false
    : true;

declare const KEY: unique symbol;
declare const CODE: unique symbol;

/**
 * WithIndex<string, 'foo'> = object with key index 'foo'.
 */
type WithIndex<
    Element,
    Key extends string,
    KeyCode extends number = KeyToNum<Key>['length']
> = KeyCode extends never
    ? never
    : {
          readonly [KEY]: Key;
          readonly [CODE]: KeyCode;
      } & {
          readonly [K in KeyCode]: Element;
      };

/**
 * Index<typeof indexedArray> = index of indexedArray.
 */
type Index<A extends { readonly [CODE]: number }> = A[typeof CODE];

/**
 * assertArrayIndex(arr, 'foo') assert that arr is array with key index 'foo'.
 */
function assertArrayIndex<A extends readonly unknown[], Key extends string>(
    array: IsKey<Key> extends true ? (IsArray<A> extends true ? A : never) : never,
    key: Key,
): asserts array is IsKey<Key> extends true
    ? IsArray<A> extends true
        ? A & WithIndex<A[number], Key>
        : never
    : never {}