Reputation: 8621
Take the following minimal example:
type BinaryOp = 'MOV'
type UnaryOp = 'ADD' | 'SUB' | 'JRO'
const BinaryOps: BinaryOp[] = ['MOV']
const UnaryOps: UnaryOp[] = ['ADD', 'SUB', 'JRO']
type Line =
{ op: BinaryOp, a: number, b: number }
| { op: UnaryOp, a: number }
And the following "pattern match":
switch (line.op) {
case 'ADD':
case 'SUB':
case 'JRO':
return `${line.op} ${line.a}`
case 'MOV':
return `${line.op} ${line.a}, ${line.b}`
}
I don't particularly like that, in order for the case to understand the op is a UnaryOp
or a BinaryOp
, I have to enumerate all the possibilities. Is there a compact(er) way to achieve this?
NOTE. Take into consideration that this is a simplified example, and there might be other kind of Op
's.
Upvotes: 4
Views: 93
Reputation: 58400
I don't believe there is any TypeScript trickiness that can be used to avoid having to enumerate all of the case
labels, as the labels are required to be values - not types.
However, you could use if
statements instead of the switch
and you could use user-defined type guards within the if
expressions.
A user-defined type guard is a function that has a return type that is a type predicate. For example, a user-defined type guard for a UnaryOp
might look like this:
function isUnaryOp(op: string): op is UnaryOp {
return op && UnaryOps.includes(op as any);
}
When used in if
statements, user-defined type guards will narrow the type:
if (isUnaryOp(line.op)) {
const op = line.op; // Inferred to be UnaryOp
} else if (isBinaryOp(line.op)) {
const op = line.op; // Inferred to be BinaryOp
} else {
const op = line.op; // Inferred to be never
}
Upvotes: 2