I have the following type declarations:
type Root = { r: string };
type A = { a: string };
type B = { b: string };
type Main = Root & (A | B);
There's the problem that the compiler can't make sure that either .a
or .b
are available at this point, because that's going to be validated within runtime. As soon as you create a variable of type Main
, you'll specify what specialized type of Main it is. So either Root & A
or Root & B
.
To make sure that .a
or .b
are accessible within that function just do the following:
type Root = { r: string };
type AB = { a?: string, b?: string };
type Main = Root & AB;
const func = (main: Main): void => {
if ('a' in main) {
main.a!
} else {
main.b!
}
}
You are right about Main
being equivalent to (Root & A) | (Root & B)
you are wrong about the interpretation of union types (|
). A union type means you can assign either type in the union to main
( so either (Root & A)
or (Root & B)
), but you can only access common properties of the union members. In this case the common member is only r
. Since main
ca be either (Root & A)
or (Root & B)
, a
might not exist on main
, so typescript prevents such an access.
To use the members that only exist on one type in the union you need a type guard. In this case an in
type-guard will work best:
const func : (main: Main) => void = main => {
if ('a' in main) {
main.a
} else {
main.b
}
}
You can read more about type guards here