type Numbers = {
a: number;
b: number;
f: number;
};
type SameKeysWithNeyTypes = SomeThingKeyOf<Numbers> & {
a: string;
b: Date;
c: null; // Error here because c is not in Numbers ?
// Error because f is missing
}
How can I do this?
Basically the keys of the SameKeysWithNeyTypes
type are validated from the keys in Numbers
but with the possibility of changing their types one by one ?
You could define it as a generic utility type HasSameKeysAs<T, U>
which always evaluates to U
, but will complain unless it has exactly the same keys as T
:
type HasSameKeysAs<
T, U extends {
[P in keyof T | keyof U]: P extends keyof T ? any : never
}> = U;
This works because U
is constrained to a type that depends on both T
and U
. We map over both the keys of T
and the keys of U
. Any key which is present in T
will be required and allowed to be anything (with any
) whereas any key which is present in U
but not T
will be forced to be the impossible never
type, which essentially prohibits it (well, it allows never
, but that's unlikely to accidentally show up... at least it didn't for your example use case).
Let's try it:
type SameKeysWithNeyTypes = HasSameKeysAs<Numbers, {
a: string;
b: Date;
}> // error!
// Property 'f' is missing in type '{ a: string; b: Date; }'
// but required in type '{ a: any; b: any; f: any; }'.
type SameKeysWithNeyyTypes = HasSameKeysAs<Numbers, {
a: string;
b: Date;
c: null;
f: 1;
}> // error!
// Types of property 'c' are incompatible.
type SameKeysWithNeyyyTypes = HasSameKeysAs<Numbers, {
a: string;
b: Date;
f: 1;
}> // okay
Looks good. Missing keys and extra keys cause errors, while a type with exactly the same keys works fine.