Recently, I've been experimenting with Typescript and decided to explore the creation of an innovative Either
type that could distinguish between success and failure scenarios. Utilizing a user-defined type guard, I managed to precisely narrow down the types of success and failure using a Symbol. However, I am puzzled as to why the value of either
in the event of a failure is displayed as the intersection type of Data & CustomError
instead of just CustomError
.
const FailureSymbol = Symbol('Failure');
interface IFailure {
symbol: Symbol;
}
type Success<T> = T
type Failure<T extends IFailure> = T
type Either<T, U extends IFailure> = Success<T> | Failure<U>
function isFailure<T, U extends IFailure>(either: Either<T, U>): either is Failure<U> {
const candidateFailure = either as Failure<U>;
return candidateFailure.symbol && candidateFailure.symbol === FailureSymbol;
}
interface Data {
data: string;
}
interface CustomFailure {
symbol: Symbol;
errorMessage: string;
}
let either: Either<Data, CustomFailure> = { data: 'success' };
if (isFailure<Data, CustomFailure>(either)) {
// within this branch, the type of either appears to be: Data & CustomFailure
console.log('failure', either);
} else {
// within this branch, the type of either seems to be: Data
console.log('success', either)
}