00925-extreme-assert-array-index

Back

// your answers

After learning from the existing solutions, I found no answer of this challenge for me, cause I think it's not a good challenge. Though there are good points to learn:

What's the flaws of this challenge IMO?

Thanks for this challenge and the points learned from it.

Solution by zhaoyao91 #23450


type Secrets = [
  1e32,2e32,3e32,4e32,5e32,6e32,7e32,8e32,9e32,10e32,
  11e32,12e32,13e32,14e32,15e32,16e32,17e32,18e32,19e32,20e32,
  21e32,22e32,23e32,24e32,25e32,26e32,27e32,28e32,29e32,30e32,
  31e32,32e32,33e32,34e32,35e32,36e32,37e32,38e32,39e32,40e32,
  41e32,42e32,43e32,44e32,45e32,46e32,47e32,48e32,49e32,50e32,
  51e32,52e32,53e32,54e32,55e32,56e32,57e32,58e32,59e32,60e32,
  61e32,62e32,63e32,64e32,65e32,66e32,67e32,68e32,69e32,70e32,
  71e32,72e32,73e32,74e32,75e32,76e32,77e32,78e32,79e32,80e32,
  81e32,82e32,83e32,84e32,85e32,86e32,87e32,88e32,89e32,90e32,
  91e32,92e32,93e32,94e32,95e32,96e32,97e32,98e32,99e32,100e32,
  101e32,102e32,103e32,104e32,105e32,106e32,107e32,108e32,109e32,110e32,
  111e32,112e32,113e32,114e32,115e32,116e32,117e32,118e32,119e32,120e32,
  121e32,122e32,123e32,124e32,125e32,126e32,127e32,128e32,129e32,130e32,
  131e32,132e32,133e32,134e32,135e32,136e32,137e32,138e32,139e32,140e32,
  141e32,142e32,143e32,144e32,145e32,146e32,147e32,148e32,149e32,150e32,
  151e32,152e32,153e32,154e32,155e32,156e32,157e32,158e32,159e32,160e32,
  161e32,162e32,163e32,164e32,165e32,166e32,167e32,168e32,169e32,170e32,
  171e32,172e32,173e32,174e32,175e32,176e32,177e32,178e32,179e32,180e32,
  181e32,182e32,183e32,184e32,185e32,186e32,187e32,188e32,189e32,190e32,
  191e32,192e32,193e32,194e32,195e32,196e32,197e32,198e32,199e32,200e32
]


type UniqIndex<S> = ToSecretsUion<BitString<S>>

type ToSecretsUion<BS, SS=Secrets, ACC=never> 
  = SS extends [infer S0, infer S1, ...infer TS] 
    ? BS extends `0${infer R}` ? ToSecretsUion<R, TS, ACC | S0>
    : BS extends `1${infer R}` ? ToSecretsUion<R, TS, ACC | S1>
    : ACC
  : never


type BitString<S, ACC extends string = ''> 
  = S extends `${infer L}${infer R}` ? BitString<R, `${ACC}${CharToBS[keyof CharToBS & L]}`> : ACC

type CharToBS 
  = CharToBS_Helper<"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_$"> 

type CharToBS_Helper<CS, BS='0000000', ACC={}> 
  = CS extends `${infer L}${infer R}`? CharToBS_Helper<R, AddBit<BS>, ACC & Record<L, BS>> : ACC

type AddBit<S> 
  = S extends `${infer R}1` ? `${AddBit<R>}0` : S extends `${infer U}0` ? `${U}1` : never



function assertArrayIndex<A extends readonly any[], K extends string>(
    array: IsTuple<A> extends true ? never : A,
    key: K) : asserts array is typeof array & Record<'key', UniqIndex<K>> & Record<UniqIndex<K>, A[number]> {}

type IsTuple<T extends readonly unknown[]> = number extends T['length'] ? false : true  

type Index<A> = A[keyof A & 'key']

Solution by okayu29 #17791

TS Playground

// 构造数组
type BuildArray<N extends number, A extends unknown[] = []> = A['length'] extends N ? A : BuildArray<N, [...A, unknown]>

// 映射字母,用于计算字符串的hash值
type HashMap = {
  a: BuildArray<1>
  b: BuildArray<2>
  c: BuildArray<3>
  d: BuildArray<4>
  e: BuildArray<5>
  f: BuildArray<6>
  g: BuildArray<7>
  h: BuildArray<8>
  i: BuildArray<9>
  j: BuildArray<10>
  k: BuildArray<11>
  l: BuildArray<12>
  m: BuildArray<13>
  n: BuildArray<14>
  o: BuildArray<15>
  p: BuildArray<16>
  q: BuildArray<17>
  r: BuildArray<18>
  s: BuildArray<19>
  t: BuildArray<20>
  u: BuildArray<21>
  v: BuildArray<22>
  w: BuildArray<23>
  x: BuildArray<24>
  y: BuildArray<25>
  z: BuildArray<26>
}

// 将哈希字符串转为哈希值
type Hash<HashString extends string, HashArray extends unknown[] = []> = HashString extends `${infer F extends keyof HashMap}${infer rest}`
  ? Hash<rest, [...HashArray, ...HashMap[F]]>
  : HashArray['length']

// 当HashString不为空时,依次判断每个字符是否符合HashMap的key值
type IsHashKeyHelper<HashString extends string> = HashString extends `${infer F}${infer rest}`
  ? F extends keyof HashMap
    ? IsHashKeyHelper<rest>
    : false
  : true

// 当HashString为空时,返回false
type IsHashKey<HashString extends string> = HashString extends '' ? false : IsHashKeyHelper<HashString>

declare const KEY: unique symbol

// 给数组 A 的类型挂载一个唯一的哈希值symbol => hashKey,以及哈希值对应的数组元素类型,hashKey => A[number]
function assertArrayIndex<A extends readonly unknown[], HashString extends string>(
  array: number extends A['length'] ? A : never, // 判断 A 是否是只读元组,是的话 A['length'] 会返回具体数字,则 array: never
  hashString: IsHashKey<HashString> extends true ? HashString : never, // 判断 hashString 是否是有效的哈希字符串
): asserts array is (number extends A['length']  // 如果 A 是只读元组, 则断言 array 为 never 类型 
  ? A & 
    { readonly [KEY]: Hash<HashString> } & // 暂存 A 的哈希值, 在 Index 工具函数中取出
    { readonly [hashString in Hash<HashString>]: A[number] } // 访问 A[Index],若 Index 为 hashKey, 则能正确访问
  : never) 
  {} // 函数体

// 取出Array的哈希值,作为遍历的索引变量的类型,这样后续访问 A[Index]
type Index<Array extends { readonly [KEY]: number }> = Array[typeof KEY]

Solution by wzp-coding #17338

// your answers
type Dictionary = 
{
  'a':'1','b':'2','c':'3','d':'4','e':'5','f':'6','g':'7','h':'8','i':'9','j':'10',
  'k':'11','l':'12','m':'13','n':'14','o':'15','p':'16','q':'17','r':'18','s':'19',
  't':'20','u':'21','v':'22','w':'23','x':'24','y':'25','z':'26', 'A':'27','B':'28',
  'C':'29','D':'30','E':'31','F':'32','G':'33','H':'34','I':'35','J':'36','K':'37',
  'L':'38','M':'39','N':'40','O':'41','P':'42','Q':'43','R':'44','S':'45','T':'46',
  'U':'47','V':'48','W':'49','X':'50','Y':'51','Z':'52', '1':'53','2':'54','3':'55',
  '4':'56','5':'57','6':'58','7':'59','8':'60','9':'61'} ;

type Digits = 
{
  '0':[],
  '1':[0],
  '2':[0,0],
  '3':[0,0,0],
  '4':[0,0,0,0],
  '5':[0,0,0,0,0],
  '6':[0,0,0,0,0,0],
  '7':[0,0,0,0,0,0,0],
  '8':[0,0,0,0,0,0,0,0],
  '9':[0,0,0,0,0,0,0,0,0] ;  
} ;

type UnPack<
    S extends string> =
    S extends `${infer R extends keyof Digits}${infer U extends string}`
    ? [...Digits[R],...UnPack<U>]
    : []  ;

type Hash<
    Input extends string,
    Increment extends readonly any[]=[],
    Container extends readonly any[]=[],
    > =
    Input extends `${infer R extends keyof Dictionary}${infer U extends string}`
    ? Hash<U,[...Increment,R],[...Container,...UnPack<`${Dictionary[R]}${Increment['length']}`>]>
    : Container['length']  ; 

declare function assertArrayIndex<
    A extends readonly any[],
    Key extends string,
    >(arr: number extends A['length'] ? A : never, key: Key):
    asserts arr is (typeof arr)&{[J in Hash<Key>|'key'] : J extends 'key' ? Hash<Key> :A[number]}  ;

type Index<
    T extends readonly any[]> =
    T['key'&keyof T]  ;

Solution by justBadProgrammer #16981

type HashMap = {a: 11, b: 12, c: 13, d: 14, e: 15, f: 16, g: 17, h: 18, i: 19, j: 20, k: 21, l: 22, m: 23, n: 24, o: 25, p: 26, q: 27, r: 28, s: 29, t: 30, u: 31, v: 32, w: 33, x: 34, y: 35, z: 36}

type Hash<S, Num extends string = '2'> =
  S extends `${infer First extends string}${infer Trail extends string}`
    ? Lowercase<First> extends keyof HashMap
      ? Hash<Trail, `${Num}${HashMap[Lowercase<First>]}`>
    : '00'
  : Num

function assertArrayIndex<A extends readonly any[], K extends string>(
    array: number extends A['length'] ? A : never,
    key: K
): asserts array is (
  number extends A['length']
    ? A &
      {[P in -1 | ToNum<`-${Hash<K>}`>]:
        P extends -1
          ? ToNum<`-${Hash<K>}`>
        : A[number]
      }
  : never
) {}

type ToNum<S extends string> =
  S extends `${infer N extends number}`
    ? number extends N
      ? never
    : N
  : never

type Index<A> = -1 extends keyof A ? A[-1] : number

Playground

Solution by teamchong #11642

// your answers
type HashMapHelper<T extends number, R extends unknown[] = []> = R['length'] extends T
  ? R
  : HashMapHelper<T, [...R, unknown]>

type HashMap = {
  '0': HashMapHelper<0>
  '1': HashMapHelper<1>
  '2': HashMapHelper<2>
  '3': HashMapHelper<3>
  '4': HashMapHelper<4>
  '5': HashMapHelper<5>
  '6': HashMapHelper<6>
  '7': HashMapHelper<7>
  '8': HashMapHelper<8>
  '9': HashMapHelper<9>
  a: HashMapHelper<1>
  b: HashMapHelper<2>
  c: HashMapHelper<3>
  d: HashMapHelper<4>
  e: HashMapHelper<5>
  f: HashMapHelper<6>
  g: HashMapHelper<7>
  h: HashMapHelper<8>
  i: HashMapHelper<9>
  j: HashMapHelper<10>
  k: HashMapHelper<11>
  l: HashMapHelper<12>
  m: HashMapHelper<13>
  n: HashMapHelper<14>
  o: HashMapHelper<15>
  p: HashMapHelper<16>
  q: HashMapHelper<17>
  r: HashMapHelper<18>
  s: HashMapHelper<19>
  t: HashMapHelper<20>
  u: HashMapHelper<21>
  v: HashMapHelper<22>
  w: HashMapHelper<23>
  x: HashMapHelper<24>
  y: HashMapHelper<25>
  z: HashMapHelper<26>
}

type Hash<T extends string, RR extends unknown[] = []> = T extends `${infer L}${infer R}`
  ? Hash<R, [...RR, ...HashMap[keyof HashMap & L]]>
  : RR['length']

type IsKeyHelper<K extends string> = K extends `${infer L}${infer R}`
  ? L extends keyof HashMap
    ? IsKeyHelper<R>
    : false
  : true

type IsKey<K extends string> = K extends '' ? false : IsKeyHelper<K>

declare const KEY: unique symbol

function assertArrayIndex<A extends readonly unknown[], K extends string>(
  array: number extends A['length'] ? A : never,
  key: IsKey<K> extends true ? K : never,
): asserts array is number extends A['length']
  ? A & { readonly [KEY]: Hash<K> } & {
      readonly [key in Hash<K>]: A[number]
    }
  : never {}

type Index<Array extends { readonly [KEY]: number }> = Array[typeof KEY]

Solution by ZangYuSong #7058

type Ord 
    = {a:1,b:2,c:3,d:4,e:5,f:6,g:7,h:8,i:9,j:10,k:11,l:12,m:13,n:14,o:15,p:16,q:17,r:18,s:19,t:20,u:21,v:22,w:23,x:24,y:25,z:26}
 
type Make<N, A extends any[] = []>
    = A['length'] extends N ? A : Make<N, [[], ...A]>

type Hash<S, A extends any[] = []> 
    = S extends `${infer L}${infer R}` ? Hash<R, [...Make<Ord[keyof Ord & L]>, ...A]> : A['length']

function assertArrayIndex<A extends readonly any[], K extends string>(
        array: number extends A['length'] ? A : never,
        key: K
    ) : asserts array is (number extends A['length'] ? A & {key: Hash<K>} & {[P in Hash<K>]: A[number]} : never) {}

type Index<Array> = Array[keyof Array & 'key']

Solution by okayu29 #6292

TS Playground.

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 {}

Solution by uid11 #946