I have a custom type like this (this code doesn't work):
type MyType =
| {
foo: string;
}
| {
foo: string;
barPre: string;
barPost: string;
}
| {
foo: string;
quxPre: string;
quxPost: string;
}
| {
foo: string;
barPre: string;
barPost: string;
quxPre: string;
quxPost: string;
};
I basically want the following objects to be valid MyType
s:
const myThing1: MyType = { foo: 'foo' };
const myThing2: MyType = { foo: 'foo', barPre: 'barPre', barPost: 'barPost' };
const myThing3: MyType = { foo: 'foo', quxPre: 'quxPre', quxPost: 'quxPost' };
const myThing4: MyType = {
foo: 'foo',
barPre: 'barPre',
barPost: 'barPost',
quxPre: 'quxPre',
quxPost: 'quxPost',
};
And the following objects should be invalid MyType
s:
const myThing5: MyType = {}; // missing 'foo'
const myThing6: MyType = { barPre: 'barPre', barPost: 'barPost' }; // missing 'foo'
const myThing7: MyType = { foo: 'foo', barPre: 'barPre' }; // if `barPre` exists, `barPost` must, too.
const myThing8: MyType = { barPre: 'barPre', barPost: 'barPost', quxPre: 'quxPre', quxPost: 'quxPost' }
I got the correct type hints, by typing it like this:
type MyType = {
foo: string;
} & ({
barPre?: undefined;
barPost?: undefined;
} | {
barPre: string;
barPost: string;
}) & ({
quxPre?: undefined;
quxPost?: undefined;
} | {
quxPre: string;
quxPost: string;
});
But this seems very tedious. Is there an easier way to achieve this?
This is due to the weird excess property checking Typescript does for unions. See https://github.com/microsoft/TypeScript/issues/36945
There is a strict union type you can define that works at least in your case. I've not tried this in earnest but you can read about it here.
Define a StrictUnion type as:
and then use that in your union
type MyType = StrictUnion<A | B | C | D>
It's then reporting errors myThing5, 6, 7 and 8 as you'd expect.