If you have a specific set of allowed values, you can achieve this by following the instructions at https://tsplay.dev/NlXgrN
type ValidateUniqueArray<T, A extends T[]> =
| A extends [infer F, ...infer L]
? (L extends Exclude<T, F>[]
? [F, ...ValidateUniqueArray<Exclude<T, F>, L>]
: [F, Exclude<T, F>?]
) : [T?, ...unknown[]]
;
type ValidateCompleteUniqueArray<T, A extends any[]> =
| [T] extends [never]
? []
: A extends [infer F, ...infer L]
? (F extends T
? [F, ...ValidateCompleteUniqueArray<Exclude<T, F>, L>]
: [T, ...ValidateCompleteUniqueArray<T, L>]
) : [T]
;
type conforms<T, V> = T extends V ? T : V;
function uniqueArray<T>() {
return {
partial<A extends T[]>(a: conforms<A, ValidateUniqueArray<T, A>>): A {
return a as A;
},
complete<A extends T[]>(a: conforms<A, ValidateCompleteUniqueArray<T, A>>): A {
return a as A;
},
}
}
let a1 = uniqueArray<1 | 2 | 3 | 4>().partial([
1, 2, 3, 4 // all good
])
let a2 = uniqueArray<1 | 2 | 3 | 4>().partial([
1, 1 // Type '1' is not assignable to type '2 | 3 | 4'.(2322)
])
let a3 = uniqueArray<1 | 2 | 3 | 4>().partial([
1, 'foo' // Type 'string' is not assignable to type '2 | 1 | 3 | 4'.(2322)
])
let b1 = uniqueArray<1 | 2 | 3 | 4>().complete([
1, 2, 3, 4 // all good
])
let b2 = uniqueArray<1 | 2 | 3 | 4>().complete([
1, 2, 3, 1 // Type '1' is not assignable to type '4'.(2322)
])
let b3 = uniqueArray<1 | 2 | 3 | 4>().complete([
1, 2, 3, 'foo' // error
])
let b4 = uniqueArray<1 | 2 | 3 | 4>().complete([
1, 2, 3 // Argument of type '[1, 2, 3]' is not assignable to parameter of type '[1, 2, 3, 4]'.
])