Reputation: 2282
I have this code to model a result type. The Failure type would contain all possible failures. 'Incorrect' is not one of them, but I nevertheless can return Incorrect as a Failure.
type Try<T, E> = Success<T> | Failures<E> | E;
// User code
type Failure = FileNotFound | NameNotFound;
class FileNotFound extends BaseFailure {}
class NameNotFound extends BaseFailure {}
class Incorrect extends BaseFailure {}
type Result<T> = Try<T, Failure>
function y() {
let x1 = x(1);
if (x1.isSuccess) {
} else {
x1.hasMany;
}
}
function x(a:number): Result<String> {
if (a>3) {
return new Success("Hallo");
} else {
return new Incorrect();
}
}
Upvotes: 1
Views: 34
Reputation: 249706
Typescript uses structural compatibility to determine type compatibility. So when it decides whether the class Incorrect
is compatible with Failure
is compares the structure of Incorrect
with the members of the Failure
union. Doing this it will find that FileNotFound
has the same structure as Incorrect
and are thus compatible.
To get around this you can add a member (preferably private
) to all classes in the union. This for example will fail:
class Success<T> { constructor(public value: T) { } isSuccess: true = true }
type Try<T, E> = Success<T> | E;
class BaseFailure {
isSuccess: false
}
type Failure = FileNotFound | NameNotFound;
class FileNotFound extends BaseFailure { private _type: "filenotfound" }
class NameNotFound extends BaseFailure { private _type: "namenotfound" }
class Incorrect extends BaseFailure { }
type Result<T> = Try<T, Failure>
function y() {
let x1 = x(1);
if (x1.isSuccess) {
} else {
}
}
function x(a: number): Result<String> {
if (a > 3) {
return new Success("Hallo");
} else {
return new Incorrect(); // error now
}
}
You can also include the private field in BaseFailure
to force implementers to specify it:
class BaseFailure<T> {
isSuccess: false
private __type: T
}
class FileNotFound extends BaseFailure<"filenotfound"> { }
class NameNotFound extends BaseFailure<"namenotfound"> { }
class Incorrect extends BaseFailure<"incorrect"> { }
Allowing some nominal typing has long been discussed (see issue) but for now this is the best we can do
Upvotes: 1