Reputation: 99
I read up about exhaustive type checking and it is really a great feature of typescript. I experimented with it and came across some odd behavior (or not fully implemented by the typescript team). This is the code I have now (which works great):
type factType = 'test-1' | 'test-2'
function doSomething(fact: factType) {
if (fact ==='test-1') {
return true;
} else if(fact === 'test-2') {
return true;
}
assertUnreachable(fact);
}
function assertUnreachable(x: never): never {
throw new Error("Didn't expect to get here");
}
But when I use a function as a branching condition, it breaks. It says "Argument of type 'string' is not assignable to parameter of type 'never' fact: "test-2"":
type factType = 'test-1' | 'test-2';
function doSomething(fact: factType) {
if (fact === 'test-1') {
return true;
} else if (isTest2(fact)) {
return true;
}
assertUnreachable(fact);
}
function isTest2(fact: factType) {
return fact === 'test-2';
}
I tried using fact as 'test-2'
but it does not work.
Does anyone know how to fix this or what the reason may be? I think it is Typescript, but I am by no means an expert!
Thanks!
Upvotes: 3
Views: 1741
Reputation: 11
I'm also pretty new to exhaustive checking, but most of the times I see it in combination with the switch
case. The following example works:
type factType = 'test-1' | 'test-2';
function doSomething(fact: factType): boolean {
switch (fact) {
case 'test-1':
return true;
case 'test-2':
return true;
default:
assertUnreachable(fact);
}
}
I guess that when you use a function that can take either 'test-1' or 'test-2' is not the same as using case
where only one value for the factType
can be evaluated at a time. By using the union (|)
you're saying "factType can only be one of these types. Either one or the other."
assertUnreachable
will make sure all the paths are covered at compile time (yay!). This means that if factType
would have an additional type, then assertUnreachable
will have a compile error.
Adding a new type results in compile error
type factType = 'test-1' | 'test-2' | 'test-3';
function doSomethingElse(fact: factType): boolean {
switch (fact) {
case 'test-1':
return true;
case 'test-2':
return false;
default:
assertUnreachable(fact); // TS2345: Argument of type '"test-3"' is not assignable to parameter of type 'never'
}
Upvotes: 1
Reputation: 4207
Use a type predicate to indicate to TS that the function checks if fact
is of type test-2
.
function isTest2(fact: factType): fact is 'test-2' {
return fact === 'test-2';
}
Upvotes: 1