By utilizing function overloading, you have the ability to achieve something along these lines:
type PersonInfoWithPets = PersonInfo & { pets: true; petNames: string[] };
type PersonInfoWithoutPets = PersonInfo & { pets?: false; petNames?: undefined };
function recordPersonInfo(options: PersonInfoWithPets): void;
function recordPersonInfo(options: PersonInfoWithoutPets): void;
function recordPersonInfo(options: PersonInfo): void {
}
recordPersonInfo({
name: 'name',
age: 0,
});
recordPersonInfo({
name: 'name',
age: 0,
pets: false,
});
recordPersonInfo({
name: 'name',
age: 0,
pets: true,
petNames: [],
});
// This errors because `pets` but no `petNames`
recordPersonInfo({
name: 'name',
age: 0,
pets: true,
});
// This errors because `petNames` but no `pets`
recordPersonInfo({
name: 'name',
age: 0,
petNames: ['hi'],
});
// NOTE: This also errors because `pets` is a boolean here, not strictly true/false
recordPersonInfo(null! as PersonInfo);
In all honesty, TypeScript may not offer a flawless solution for this issue. In this particular scenario, one could argue that "the presence (and emptiness) of petNames
should replace the need for the pets
field." Nevertheless, we managed to work around it with two overloads, though more complex situations might necessitate additional overloads.
Naturally, you could opt to thoroughly document the function and throw an error if incorrect input is still provided.