30178-hard-unique-items

Back

type Equal<A, B> = (<T>() => T extends A ? 1 : 2) extends <T>() => T extends B
  ? 1
  : 2
  ? true
  : false;

type IsUniqueItems<Arr extends any[], MayByUnique = Arr[0]> = Arr extends [
  infer F,
  ...infer Rest
]
  ? Equal<MayByUnique, F> extends true
    ? IsUniqueItems<Rest, MayByUnique>
    : false
  : true;

type t = IsUniqueItems<[1, 2]> extends true ? true : never;

function uniqueItems<const Arr extends any[]>(
  arr: IsUniqueItems<Arr> extends true ? Arr : never
) {}

uniqueItems([1, "1"]);

Solution by Gravity2333 #37285


namespace U {
  const UniSym:unique symbol = Symbol();

  export type CheckUnique<T extends readonly any[],RSLT extends readonly any[] = [],S = T> = 
  T extends [infer F, ...infer R extends readonly any[]]
    ? CheckUnique<R, readonly [...RSLT, F extends RSLT[number]? {"ErorHint":"duplicate array element.", [UniSym]:any} : F],S>
    : RSLT extends S ? RSLT : RSLT;

}


function uniqueItems<const T extends readonly any[]>(items: U.CheckUnique<T>): typeof items {return items;}

Solution by sciencefunq #37081

type Check<A, E = never, R extends unknown[] = []> = A extends [infer F, ...infer T]
  ? Check<T, E | F, [...R, [F] extends [E] ? never : F]>
  : R;

function uniqueItems<const T>(items: Check<T> & T): T {
  return items;
}

// hack for TS 5.2.x and older:
// function uniqueItems<T extends readonly [] | [V, ...V[]], V extends PropertyKey | boolean | bigint | null | undefined>(items: Check<T> & T): T {
//   return items;
// }

Solution by alexandroppolus #35024

type UniqueItems<T, M = never> = T extends [infer F, ...infer R] ? F extends M ? never : [F, ...UniqueItems<R, M | F>] : []

function uniqueItems<const T>(items: T extends UniqueItems<T> ? T : UniqueItems<T>) {
  return items
}

Solution by Mantou1233 #34510

type Ensure<T,  _P extends unknown[] = []> =
  T extends [ infer F, ...infer R ]
    ? Ensure<R, [ ..._P, F extends _P[number] ? never : F ]>
    : _P extends T ? _P : _P

function uniqueItems<const T>(items: Ensure<T>) {
  return items
}

Solution by lvjiaxuan #33600

type UniqueItemsTuple<T>
  = {[I in keyof T]: T[I] extends PreviousItems<T, I> ? { error: `Item duplicated at position ${I & string}` } : T[I]}

type PreviousItems<T, I extends keyof T>
  = ReturnType<<P extends Replicate<I & string>, C extends {[I in keyof P]: T[I & keyof T]}> () => C[number]>

// make tuple of any, minimize iterations to handle tuples of all lengths
type Replicate<N extends number | string, T extends any[] = []>
  = `${N}` extends `${infer L}${infer R}` ? Replicate<R, [...T, ...T, ...T, ...T, ...T, ...T, ...T, ...T, ...T, ...T, ...([[], [any], [any, any], [any, any, any], [any, any, any, any], [any, any, any, any, any], [any, any, any, any, any, any], [any, any, any, any, any, any, any], [any, any, any, any, any, any, any, any], [any, any, any, any, any, any, any, any, any]] & Record<string, []>)[L]]>
  : T

declare const uniqueItems: <const T>(items: T extends UniqueItemsTuple<T> ? T : UniqueItemsTuple<T>) => typeof items

Playground

Solution by teamchong #33165

type GenerateHintTuple<T, _U = never> = T extends [infer First, ...infer Rest]
  ? [First extends _U ? never : First, ...GenerateHintTuple<Rest, First | _U>]
  : []

function uniqueItems<const T>(items: T extends GenerateHintTuple<T> ? T : GenerateHintTuple<T>) {
  return items
}

Solution by Sun79 #33118