Reputation: 1014
This my code :
interface Props {
id: string;
name: string;
age: number;
};
const keysOfProps: (keyof Props)[] = ['id', 'name']; // This should show a warning because the ``age`` string is missing
I expect this keysOfProps to be of type ['id, 'name', 'age']
and not ('id' | 'name' | 'age')[]
How can I achieve this ?
Upvotes: 1
Views: 87
Reputation: 3666
While it's not possible to define the desired type for the tuple, it is possible to assert that the type for a given tuple matches all of the keys. We can use a variation on the Pick
utility type to build a type from Props
which contains the keys defined by a tuple and then we can assert that the result is the same as the original Props
. The solution takes a few steps and a function call, but the following should work:
type Tuple<T> = readonly T[]
type TuplePick<T, K extends Tuple<keyof T>> = Pick<T, K[number]> // like Pick, but we pass a tuple of the keys
type IfEqualThen<T, U, R> = T extends U ? (U extends T ? R : never) : never // 'R' if T and U are mutually assignable
type AssertAllKeys<P, T extends Tuple<keyof P>> = IfEqualThen<P, TuplePick<P, T>, T>
function assertKeys <P> () {
return function <T extends Tuple<keyof P>> (value: AssertAllKeys<P, T>): T
{
return value
}
}
interface Props {
id: string;
name: string;
age: number;
};
const keys1 = assertKeys<Props>()(['id', 'name'] as const) // Error, argument not assignable to never
const keys2 = assertKeys<Props>()(['id', 'name', 'age'] as const) // Works
Upvotes: 1