Utilizing the never
keyword in this scenario is a smart way to establish two distinct "configurations" for the User entity - one with a Place attribute and another with a Loc attribute. The Place type mandates an unspecified latLng property, whereas the Loc type requires an undefined address property. A User can be described as ({name: string} & Place) | ({name: string} & Loc), meaning that while all three fields exist in both Place and Loc types within the union, either address or latLng will remain undefined based on the presence of the other.
type Place = {
address: string;
latLng?: never;
};
type Loc = {
latLng: string;
address?: never;
};
type User = {
name: string;
} & (Place | Loc);
function test1() {
// Valid, latLng is undefined
const user: User = { name: "User", address: "1234 Somewhere St" };
const { address, latLng, name } = user;
}
function test2() {
// Valid, address is undefined
const user: User = { name: "User", latLng: "123,456" };
const { address, latLng, name } = user;
}
function test3() {
// Invalid, both address and latLng are present
const user: User = {
name: "User",
latLng: "123,456",
address: "1234 Somewhere St",
};
const { address, latLng, name } = user;
}
(Playground link)
This approach defines a type where either address or latLng can be specified but not both. By deconstructing the values from the user object, you can access both properties, with one always being undefined.