While it may not be feasible to achieve this solely through types, implementing a functional abstraction could serve the purpose of validating as per your requirements.
By constructing the validator function based on the initial union type, it will mandate that when the argument object is passed to it during invocation, the first
property's type must align with the initial union type, and the second
property's type should belong to the union type excluding the first one.
The key advantage of this method lies in ensuring that the resulting value retains the exact type of the input object, rather than combining all properties from the union except the first.
Try out the TypeScript Playground
function createValidator <T>() {
return <First extends T, Second extends Exclude<T, First>>(input: { first: First; second: Second; }) {
return input;
};
}
const validate = createValidator<1 | 2 | 3>();
validate({first: 1, second: 1}); /*
^^^^^^
Type '1' is not assignable to type '2 | 3'.(2322) */
validate({first: 1, second: 2}); // { first: 1; second: 2; }