'Can I specify a recursive property rule based on whether or not it is present in a descendant?
I want to specify a type called Item which has the possibility of being nested as follows:
Either
type Item {
...;
item: Item;
answer: {
...;
//(does not contain property "item")
}
}
OR
type Item {
...;
//(does not contain property "item")
answer: {
...;
item: Item
}
}
i.e. either the Item type can have item:Item as a direct child property, or as a grandchild below the property answer but not both.
Context: I'm trying to implement the item property of the QuestionnaireResponse resource from the FHIR specification
Solution 1:[1]
This should do the trick:
type FirstOrSecondLayer<T, I, A extends string> = XOR<T & I, T & Record<A, I>>
type Expand<T> = T extends infer O ? { [K in keyof O]: O[K] } : never;
type Without<T, U> = { [P in Exclude<keyof T, keyof U>]?: never };
type XOR<T, U> = (T | U) extends object ? (Without<T, U> & U) | (Without<U, T> & T) : T | U;
type Item = Expand<FirstOrSecondLayer<{
a: string
b: string
answer: {
c: string
}
},{
item: Item
}, "answer">
>
There is a lot of stuff going on here. I added some extra properties a, b and c for demonstration purposes. You pass the type without item as T. I is the exclusive object type. And for A you pass the name of the property which nests I.
I used the Expand type from here and XOR from here.
Here are some test cases:
const t1: Item = {
a: "",
b: "",
item: {} as Item,
answer: {
c: ""
}
} // works
const t2: Item = {
a: "",
b: "",
answer: {
c: "",
item: {} as Item,
}
} // works
const t3: Item = {
a: "",
b: "",
item: {} as Item,
answer: {
c: "",
item: {} as Item, // error
}
}
Let me know if this works for your use case.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 |
