Is there a way to use a guard clause for a single logical-or pattern operand in a Dart switch statement?
For example, I'd like to do something like the following, but it does not compile.
switch (x) {
case int _ || (String value when int.tryParse(value) != null):
// ...
}
This can be solved by moving the first operand into a dedicated fall-through branch, but this is not possible in switch expressions or with logical-and.
There is no way to have a with
clause inside a pattern.
What you can do is to introduce a helper extension getter, because object patterns can access getters, including extension getters:
extension on String {
int? get tryParsedAsInt => int.tryParse(this);
}
Then you can write:
case int _ || String(tryParsedAsInt: _?): ...
which matches if the value is an int
or a String
where int.tryParse(string)
is not null
.
This even allows you to capture the integer value as a variable and use it in the case body:
case int value || String(tryParsedAsInt: var value?):
print(value.toRadixString(16));
This works in switch expressions too:
extension on String {
int? get tryParsedAsInt => int.tryParse(this);
}
int? asInt(Object input) => switch (input) {
int value || String(tryParsedAsInt: var value?) => value,
bool value => value ? 1 : 0,
_ => null,
};
void main() {
print(asInt(42)); // 42
print(asInt("42")); // 42
print(asInt(true)); // 1
print(asInt("X")); // null
}
Another option is to have two cases. If the computation inside the cases is complicated, and you don't want to duplicate it, you can use a helper function.
void helper(Object x) { ... }
switch (x) {
case int _ : helper(x);
case String s when int.tryParse(s) != null : helper(x);
// ...
}
Or restructuring the code like:
if (switch (x) {
int _ => true,
String s when int.tryParse(s) != null => true,
_ => false,
}) {
... do something ...
} else {
// other cases.
}
That's really just
if (x is int || x is String && int.tryParse(x) != null) {
... do something ...
}
with extra steps. Maybe just because this pattern is a simple example, it works for more complicated patterns too.
Yet another option for having the same pattern match two different types is to give it an extra null
value to match against, so it can bind two different variables in all cases:
switch ((input, null)) {
case (int? v?, String? s) ||
(String? s?, int? v) when s == null || int.tryParse(s) != null:
return s ?? v;
}
A little contrived, I'd try to split it into two patterns instead before I tried something like this.