Reputation: 13
I want to create an interface that limits me to the values received from another, My Original thought was to derive from the values such as
interface Foo = {
id: string
}
interface Bar = {
[values of id]
}
const FooObject: Foo = {
id: "what",
}
const BarObject: Bar = {
what: "whatever" // Pass
wha: "whatever" // Fail
}
Then tried the other way around and failed as well
interface Bar = {
[ANY KEY]: string | number;
onClick: () => void; // need this since react
}
interface Foo = {
id: [keys of Bar]
}
const BarObject: Bar = {
what: "whatever",
wha: "whatever" ,
}
const FooObject: Foo = {
id: "what", // will allow only "what" | "wha"
}
Please help me if this is even possible, I'm pretty sure I've seen a solution like this using generics
Upvotes: 1
Views: 1365
Reputation: 33931
TypeScript does not infer literal values in object types, so the type of id
in FooObject
is simply string
(and not "what"
).
You can use a const assertion to force the compiler to infer literal, readonly values, like in the example below. By doing so, you can then use the inferred type of FooObject
in place of the constrained generic T
in Bar
to achieve the goal that you described.
See also:
- the type utility
Record<Keys, Type>
- the
satisfies
operator
type Foo = {
id: string;
};
type Values<T> = T[keyof T];
type Bar<T extends Record<PropertyKey, string>> = Record<Values<T>, unknown>;
const FooObject = {
id: "what",
} as const satisfies Foo;
//^^^^^^^^
// Using a const assertion forces the compiler to infer literal values
const BarObject = {
what: "whatever", // OK
wha: "whatever", /* NOK
~~~~~~~~~~~~~~~
Type '{ what: string; wha: string; }' does not satisfy the expected type 'Bar<{ readonly id: "what"; }>'.
Object literal may only specify known properties, but 'wha' does not exist in type
'Bar<{ readonly id: "what"; }>'. Did you mean to write 'what'? (1360) */
} satisfies Bar<typeof FooObject>;
Upvotes: 3