I have a similar type defined as follows:
interface Foo {
bar: string
baz: number
}
My goal is to ensure that both members are either present or neither. I attempted type X = Foo | {}
, but encountered the error
property 'bar' does not exist on type X
when trying to access the property (the actual result being undefined
). After some exploration, I found that this type achieves the desired behavior:
type X = Foo | { [k in keyof Foo]?: undefined }
This approach works well for my current scenario, but it could become more complex with additional types. Is there a more concise method to achieve object access without triggering the
property 'bar' does not exist on type X
error, without resorting to type guards or casting to any
?
Solution using never
A suggestion in the comments proposed using Record<keyof Foo, never>
, however, this did not produce the expected result. A second suggestion of
Partial<Record<keyof Foo, never>>
yielded success for the original problem.
interface Foo {
a: number
}
type X = Foo | Record<keyof Foo, never>
const x: X = {} // Error: Type '{}' is not assignable to type 'X'.
console.log(x.a)
type Y = Foo | Partial<Record<keyof Foo, never>>
const y: Y = {}
console.log(y.a)
Extended Example with multiple types
Expanding on my initial question regarding usage with multiple types, here is an example:
interface X {
a: string
b: string
}
interface Y {
c: string
d: string
}
type Z = X | Y | Partial<Record<keyof (X & Y), never>>
// These assignments should trigger errors, but they don't.
const z: Z = {a: "x", b: "x", c: "x"}
const z: Z = {a: "x", b: "x", c: "x", d: "x"}
Interestingly, the last assignments seem to work due to X | Y
behavior, rather than the record. This behavior persists even if I use type
instead of interface
. It appears that this may be the most optimal solution available.