So for the following simplified example with a union of two interfaces with a "type" discriminator:
type ABBA = A | B;
interface A { type: 'a', argForA: string }
interface B { type: 'b', argForB: boolean }
I would like to be able to construct an object but limit its keys to the type discriminators - so
const config = {
a: 123,
b: 234,
c: 345, // should error as c is not a type discriminator of ABBA
}
What i'm after is essentially the opposite of the Record<A,B>
utility type, but i can't see anything in the utility type documentation that functions this way.
I have tried searching online for an answer, but the answers are clouded by an overwhelming number of tutorials on how to use unions on interfaces.
CodePudding user response:
Consider this:
type ABBA = A | B;
interface A { type: 'a', argForA: string }
interface B { type: 'b', argForB: boolean }
type Values<T> = T[keyof T]
type OnlyDiscriminators = Values<ABBA>
const config: Record<OnlyDiscriminators, number> = {
a: 123,
b: 234,
c: 345, // error
}
Values
- obtains a union of common/shared props of A
and B
. Since discriminator should be shared property, Values returns a
| b
Record<OnlyDiscriminators, number>
creates a hash map data structures where OnlyDiscriminators
represents keys and number
represents values.
However, your A
and B
interfaces might have more props in common.
Consider this example:
type ABBA = A | B;
interface A { type: 'a', argForA: string, id: string }
interface B { type: 'b', argForB: boolean, id: string }
type Values<T> = T[keyof T]
type GetDiscriminators<D extends string, Obj extends Record<D, unknown>> = Values<Pick<Obj, D>>
type OnlyDiscriminators = GetDiscriminators<'type', ABBA>
const config: Record<OnlyDiscriminators, number> = {
a: 123,
b: 234,
c: 345, // error
}
CodePudding user response:
interface A {
type: 'a',
a: number;
}
interface B {
type: 'b',
b: string;
}
type AORB = A | B;
const x: AORB = {
type: 'b',
b: 's'
}
const y: AORB = {
type: 'a',
a: 2
}
// Will not compile
const z: AORB = {
type :'b',
a: 2
}
// Will not compile
const n: AORB = {
type :'a',
b: "2"
}
This looks very similar to your own suggestion, and it works just fine.