Reputation: 21157
I'm playing around with Typescript and was trying to see if I could create the concept of an Either
type to represent success and failure paths. I've created a user-defined type guard to explicitly narrow the types of success/failure through the use of a Symbol. I can't figure out why the value of either
in the isFailure branch is shown as being the intersection type of Data & CustomError
as opposed to 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)) {
// inside this branch the type of either is shown to be: Data & CustomFailure
console.log('failure', either);
} else {
// inside this branch the type of either is show to be: Data
console.log('success', either)
}
Upvotes: 0
Views: 367
Reputation: 51659
why the value of either in the isFailure branch is shown as being the intersection type of Data & CustomError as opposed to just CustomError
because isFailure
check follows immediately after either
was initialized with a value that has Data
type. The compiler infers types and uses control flow analysis when appropriate, so in this case it remembers that either
value has Data
type. You can see it if you try to assign either
to something declared as Data
, you get no error:
const either2: Data = either; //no error, either has `Data` type here
When either
type is not narrowed, it works as expected. Control flow analysis stops at function boundaries, so if you have the same code inside the function it will be inferred as CustsomFailure
only:
function test(either: Either<Data, CustomFailure>) {
if (isFailure<Data, CustomFailure>(either)) {
// here either is CustomFailure
console.log('failure', either);
} else {
// inside this branch the type of either is show to be: Data
console.log('success', either)
}
}
Upvotes: 2