I'm closing this as a duplicate of this question, but I'll translate the code for this question below, so you can see it applied. Please read the other answer for the caveats and suggestions here. Good luck!
interface User {
id: number
name: string
}
type Cons<H, T extends readonly any[]> = H extends any ? T extends any ?
((h: H, ...t: T) => void) extends ((...r: infer R) => void) ? R : never : never : never;
type Prev = [never, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19];
// illegally recursive, use at your own risk
type UnionToAllPossibleTuples<T, U = T, N extends number = 15> = T extends any ?
Cons<T, Exclude<U, T> extends infer X ? {
0: [], 1: UnionToAllPossibleTuples<X, X, Prev[N]>
}[[X] extends [never] ? 0 : 1] : never> :
never;
type AllPossibleTuplesOfUserKeys = UnionToAllPossibleTuples<keyof User>;
const allUserFields: AllPossibleTuplesOfUserKeys = ["id", "name"]; // okay
const missing: AllPossibleTuplesOfUserKeys = ["id"]; // error
const redundant: AllPossibleTuplesOfUserKeys = ["id", "id", "name"]; // error
const extra: AllPossibleTuplesOfUserKeys = ["id", "name", "oops"]; // error
type NoRepeats<T extends readonly any[]> = { [M in keyof T]: { [N in keyof T]:
N extends M ? never : T[M] extends T[N] ? unknown : never
}[number] extends never ? T[M] : never }
const verifyArray = <T>() => <U extends NoRepeats<U> & readonly T[]>(
u: (U | [never]) & ([T] extends [U[number]] ? unknown : never)
) => u;
const verifyUserKeyArray = verifyArray<keyof User>()
const allUserFieldsGeneric = verifyUserKeyArray(["id", "name"]); // okay
const missingGeneric = verifyUserKeyArray(["id"]); // error
const redundantGeneric = verifyUserKeyArray(["id", "id", "name"]); // error
const extraGeneric = verifyUserKeyArray(["id", "name", "oops"]); // error
// this type contains updatable fields and can be given to api/client interface
type UserUpdate = Pick<User, "name">
// this should always contain all keys to keep it in sync
const updateableFields: UnionToAllPossibleTuples<keyof UserUpdate> = ['name']
// simple example for using the array to just update updatable fields
function updateUser(user: User, update: UserUpdate) {
updateableFields.forEach(field => {
user[field] = update[field];
});
// ...
}
Link to code