You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
If-case statements have been a huge time-saver when deconstructing JSON values, but I was a little surprised when I couldn't mix a regular boolean test with a pattern test.
This happened when I was checking for the status of a resource in a REST API. I tried the following:
if (response.statusCode ==200&&jsonDecode(response.body) case {'active':true}) {
// Continue with valid flow
}
At first, it returned a warning that was somewhat cryptic at the time, The matched value type 'bool' can never match the required type 'Map<Object?, Object?>'. By having other eyes on the code, I was told that it was being parsed as (response.statusCode == 200 && jsonDecode(response.body)) case {'active': true}, which made the error understandable. && returns a bool, and that bool was being tested but using response.statusCode == 200 && (jsonDecode(response.body) case {'active': true}) only made things break more.
Proposal
We could handle the pattern test as if it were a boolean with some restrictions. This should also only be permitted in a context block where the possible captured variables are. That restriction should prevent the usage of variables not initialized by failing the pattern.
bool test =jsonDecode(response.body) case {'active':true, 'value':String value};
print('Response had value $value'); // Use of value without checking for the test result shouldn't happen
But we could accept it where a boolean would be expected.
if (response.statusCode ==200&&
(jsonDecode(response.body) case {'active':true, 'value':String value})) {
print('Response had value $value'); // Here value is guaranteed to be initialized
}
We could also accept it in a when guard, being it on if-case or also in switch:
if (response caseResponse(statusCode:200, :var body) whenjsonDecode(body) case {'active':true, 'value':String value}) {}
String?extractValue(Response response) =>switch(response) {
Response(statusCode:200, :var body) whenjsonDecode(body) case {'active':true, 'value':String value}
=> value,
_ =>null
};
As this feature also permits some calculations to happen over the captured variables that are exposed to when, it could prevent some code duplication or hard-to-reason code flow as we would enter in a case that we don't want when it should continue to the next one.
Alternative
In cases where the calculation has no arguments, it could be provided via extensions:
Sounds related to #3059 (case-checks in conditional expression, and then why not any expression) and/or #2433 (allow other selectors than getters in object patterns).
The json getter is actually what I'd still recommend, I'd just make it Object? toJson() => jsonDecode(body) with #2433:
if (response caseResponse(statusCode:200, toJson(): {...}) { ... }
If we allow (expr case pattern when expr) (optional when clause, outer parentheses needed in most contexts other than <expression>), then it would be:
if (response.statusCode ==200&& (jsonDecode(response.body) case { ... })) { ... }
The parentheses are important since we need to know where the switched-on-value expression starts.
We need the parentheses afterwards too, to correctly parse
If-case statements have been a huge time-saver when deconstructing JSON values, but I was a little surprised when I couldn't mix a regular boolean test with a pattern test.
This happened when I was checking for the status of a resource in a REST API. I tried the following:
At first, it returned a warning that was somewhat cryptic at the time,
The matched value type 'bool' can never match the required type 'Map<Object?, Object?>'
. By having other eyes on the code, I was told that it was being parsed as(response.statusCode == 200 && jsonDecode(response.body)) case {'active': true}
, which made the error understandable.&&
returns abool
, and thatbool
was being tested but usingresponse.statusCode == 200 && (jsonDecode(response.body) case {'active': true})
only made things break more.Proposal
We could handle the pattern test as if it were a boolean with some restrictions. This should also only be permitted in a context block where the possible captured variables are. That restriction should prevent the usage of variables not initialized by failing the pattern.
But we could accept it where a boolean would be expected.
We could also accept it in a
when
guard, being it on if-case or also in switch:As this feature also permits some calculations to happen over the captured variables that are exposed to
when
, it could prevent some code duplication or hard-to-reason code flow as we would enter in a case that we don't want when it should continue to the next one.Alternative
In cases where the calculation has no arguments, it could be provided via extensions:
I couldn't find an equivalent alternative for cases where an argument is needed.
The text was updated successfully, but these errors were encountered: