I'm trying to define a type for an object, such that every value of the object has the same type, and must have the same keys. I'd like to be able to do this where every value has the same keys, defined as the union of the types of all the values.
const test = {
a: { // I'd like to see a type error on this line
x: 1,
},
b: {
x: 1,
y: 2,
},
}
It's possible when you define the type in advance:
interface IA {
[key: string]: {
x: number;
y: number;
}
}
const test: IA = {
a: { // now we get a type error because we don't have y on this object
x: 1,
},
b: {
x: 1,
y: 2,
},
};
I can get something close with code like this:
const test = <T>(x: { [key: string]: { [key in keyof T]: string } }) => true;
Alternatively, in a function we can infer the type, but then the problem is that it doesn't take a union of all the types, but only the first in the object:
const test = <T>(x: { [key: string]: { [key in keyof T]: number } }) => true;
const x = test({
a: {
x: 1,
},
b: {
x: 1,
y: 2, // now we get a type error here
},
})
The type error here is:
Type '{ x: number; y: number; }' is not assignable to type '{ x: number; }'. Object literal may only specify known properties, and 'y' does not exist in type '{ x: number; }'.
I don't really see how this can be done in typescript, and I suspect it isn't possible - does anyone have any suggestions?
That's a great answer from @TitianCernicova-Dragomir, here:
type UnionKeys<U> = U extends U ? keyof U : never;
const test = <T>(x: T & Record<keyof T, Record<UnionKeys<T[keyof T]>, number>>) => true;
const x = test({
a: {
x: 1,
},
b: {
x: 1,
y: 2,
},
})