Comments (12)
You can use a typedef to accomplish this. e.g.
typedef Nullable<T> = T?;
typedef NullableString = String?;
void test(Type type) {
switch(type) {
case const (Nullable<int>): print("int?");
case NullableString: print("String?");
default: print("$type");
}
}
Note that for the generic typedef, you need to wrap it in const
as shown above.
from language.
@lukehutch You should probably consider rewriting this code into something like:
super.value = newValue;
// Asynchronously write the new value through to SharedPreferences
unawaited(switch (newValue) {
null => _sharedPreferences!.remove(sharedPreferencesKey),
int() => _sharedPreferences!.setInt(sharedPreferencesKey, newValue),
bool() => _sharedPreferences!.setBool(sharedPreferencesKey, newValue),
double() => _sharedPreferences!.setDouble(sharedPreferencesKey, newValue),
String() => _sharedPreferences!.setString(sharedPreferencesKey, newValue),
List<String>() => _sharedPreferences!.setStringList(sharedPreferencesKey, newValue),
_ => throw Exception('....'),
});
In general dispatching on runtimeType
is not something you should be doing.
from language.
@munificent @mraleph @leafpetersen @mateusfccp Thank you so much for stepping me through all of this. You guys are amazing. I haven't come across another development community before that was so committed to answering questions in helpful ways. Much appreciated.
@munificent I hadn't upgraded to Dart 3.0 previously, but I have now, and I love the new features.
from language.
...so to accomplish this, I need to change switch into a series of if-statements of the form
if (type is int?) {...}
.
This won't work anyway. type is int?
will ever fail, because type
is actually [sic] Type
. If you try to check type == int?
, it will also raise an error.
The only way to check in a if
is to use a helper function Type typeOf<T>() => T
and check for if (type == typeOf<int?>())
.
With a switch, however, it won't work, as if's expecting an constant pattern.
from language.
@lukehutch int()
is an object pattern: it matches when newValue
is subtype of int
(e.g. it is an equivalent of newValue is int
).
This works for the setter, but unfortunately not when getting values from
SharedPreferences
in the constructor. Do you have suggestions for how to handle this?
You could pattern match on the type of this
, rather than it's runtimeType
:
switch (this) {
case PersistentValueNotifier<int?>():
value = _sharedPreferences!.getInt(sharedPreferencesKey) as T? ?? initialValue;
case PersistentValueNotifier<String?>():
// ...
}
There might be few other ways to refactor this (e.g. you can make constructor private and make static methods producing specific types of notifiers: one for integers, one for strings, etc), but this can be too much engineering for too little gain.
I think the shared preferences API itself should have been strongly typed, something like:
sealed class SharedPreferencesKey<T> {
final String key;
T getFrom(SharedPreferences p);
void setOn(SharedPreferences p, T? value);
}
class StringKey extends SharedPreferencesKey<String> {
// ...
}
Then this type of code you have would be easier to write. But I digress.
from language.
@mateusfccp @leafpetersen Thanks for explaining, I didn't know any of that!
I expected case int?
to work, since this works:
switch (type) {
case List<int?>:
}
I'll close this, thanks for the information!
from language.
switch (type) { case List<int?>: }
That doesn't work either. For patterns, we only allow a pretty small subset of the expression grammar for constants. It's basically just identifiers. List<int?>
is a valid type literal expression, but it's not a valid constant pattern. This is entirely a syntactic restriction (because we need to avoid ambiguity between patterns and expressions inside patterns).
This does work:
switch (type) {
case const (List<int?>):
}
But this doesn't:
switch (type) {
case const (int?):
}
Because int?
isn't a valid type literal expression. Type literals support most kinds of type annotations, but not all of them. (The other big missing one is function types. There's no type literal syntax for a specific function type.)
As @leafpetersen says, a typedef will help.
from language.
switch (type) { case List<int?>: }That doesn't work either. For patterns, we only allow a pretty small subset of the expression grammar for constants. It's basically just identifiers.
List<int?>
is a valid type literal expression, but it's not a valid constant pattern. This is entirely a syntactic restriction (because we need to avoid ambiguity between patterns and expressions inside patterns).
@munificent I have the following code working fine in my Dart project:
@override
set value(T newValue) {
// Update the `ValueNotifier` superclass with the new value
super.value = newValue;
// Asynchronously write the new value through to SharedPreferences
switch (runtimeType) {
case PersistentValueNotifier<int>:
case PersistentValueNotifier<int?>:
if (newValue == null) {
unawaited(_sharedPreferences!.remove(sharedPreferencesKey));
} else {
unawaited(_sharedPreferences!
.setInt(sharedPreferencesKey, newValue as int));
}
break;
case PersistentValueNotifier<bool>:
case PersistentValueNotifier<bool?>:
if (newValue == null) {
unawaited(_sharedPreferences!.remove(sharedPreferencesKey));
} else {
unawaited(_sharedPreferences!
.setBool(sharedPreferencesKey, newValue as bool));
}
break;
case PersistentValueNotifier<double>:
case PersistentValueNotifier<double?>:
if (newValue == null) {
unawaited(_sharedPreferences!.remove(sharedPreferencesKey));
} else {
unawaited(_sharedPreferences!
.setDouble(sharedPreferencesKey, newValue as double));
}
break;
case PersistentValueNotifier<String>:
case PersistentValueNotifier<String?>:
if (newValue == null) {
unawaited(_sharedPreferences!.remove(sharedPreferencesKey));
} else {
unawaited(_sharedPreferences!
.setString(sharedPreferencesKey, newValue as String));
}
break;
case PersistentValueNotifier<List<String>>:
case PersistentValueNotifier<List<String>?>:
if (newValue == null) {
unawaited(_sharedPreferences!.remove(sharedPreferencesKey));
} else {
unawaited(_sharedPreferences!
.setStringList(sharedPreferencesKey, newValue as List<String>));
}
break;
default:
throw Exception('Type parameter not supported: $runtimeType');
}
}
from language.
@mraleph thanks -- what exactly does this int()
syntax do?
This works for the setter, but unfortunately not when getting values from SharedPreferences
in the constructor. Do you have suggestions for how to handle this?
from language.
Thank you everyone for your valuable suggestions. I was able to improve the code quality substantially. These are some awesome new features for Dart!
from language.
PS one final question, @mraleph what is wrong with using runtimeType
? Is that not available in production builds, or something?
from language.
@munificent I have the following code working fine in my Dart project:
Oh, wait. Have you not upgraded to Dart 3.0 yet? That code might work before Dart 3.0, but won't when you upgrade. The object patterns @mraleph suggested are Dart 3.0 features.
PS one final question, @mraleph what is wrong with using
runtimeType
? Is that not available in production builds, or something?
It's available but runtimeType
is slow and breaks Liskov substitution. Consider:
int iterableOrMapLength(Object iterableOrMap) {
...
}
This function should return the number of elements or entries in the object if you give it an Iterable or Map. For example:
main() {
print(iterableOrMapLength([1, 2, 3]);
}
This should print "3". If you implement that function using runtimeType
:
int iterableOrMapLength(Object iterableOrMap) {
if (iterableOrMap.runtimeType == Iterable) {
return (iterableOrMap as Iterable).length;
} else if (iterableOrMap.runtimeType == Map) {
return (iterableOrMap as Map).length;
} else {
throw ArgumentError('Must be Iterable or Map.');
}
}
Then the example call from main()
does not work. The object you're passing it has a runtime type of List
, not Iterable
. Those are different types, so comparing runtime types using ==
will return false
.
What you really want to ask is "Is this object an instance of Iterable or any subtype of Iterable?" To answer that, you just want a normal is
test:
int iterableOrMapLength(Object iterableOrMap) {
if (iterableOrMap is Iterable) {
return iterableOrMap.length;
} else if (iterableOrMap is Map) {
return iterableOrMap.length;
} else {
throw ArgumentError('Must be Iterable or Map.');
}
}
Object and variable patterns in switches do is
tests too, so this also works:
int iterableOrMapLength(Object iterableOrMap) {
switch (iterableOrMap) {
case Iterable():
return iterableOrMap.length;
case Map():
return iterableOrMap.length;
default:
throw ArgumentError('Must be Iterable or Map.');
}
}
Or:
int iterableOrMapLength(Object iterableOrMap) {
switch (iterableOrMap) {
case Iterable iterable:
return iterable.length;
case Map map:
return map.length;
default:
throw ArgumentError('Must be Iterable or Map.');
}
}
Or if you really want to go whole hog and use all the new 3.0 stuff:
int iterableOrMapLength(Object iterableOrMap) => switch (iterableOrMap) {
Iterable(:var length) || Map(:var length) => length,
_ => throw ArgumentError('Must be Iterable or Map.')
};
In general, runtimeType
is almost never what you want. 99% of the time, you want is
tests on instances.
from language.
Related Issues (20)
- check null value of property nested should be cast not nullable HOT 2
- Promote `obj` to non-null when checking `obj?.field == value` HOT 3
- Trailing Parameter HOT 6
- Dart Implicit Static Access HOT 5
- How do we handle deferred imports in enhanced parts? HOT 1
- Simple parts with imports HOT 6
- Expressions that are "as constant as possible" HOT 17
- Switching on bounded generic types HOT 4
- Add AI issue summary HOT 9
- Type parameters with default value for classes HOT 7
- Reintroduce varargs parameter in functions (in limited capacity) HOT 5
- implement an 'implies' logical operator HOT 9
- Some "reach the end" errors are not reported. Fix it, or twist the spec? HOT 11
- Allow `case` condition in return statement HOT 3
- Why can't I write a guard clause for each pattern in the guard clause of pattern matching? HOT 5
- Add `default` Keyword and New `::default` Operator for Using Default Parameters HOT 1
- Generic type as function return type can't work HOT 2
- Trying to get `this` type is incredibly unweildy. Ref: `Self` from Rust HOT 1
- Dart Macro Augmentation Incorrectly Adds `abstract` to `sealed` Class , while using it with dart run
- Tree-shaking not producing the same result depending on whether Dart code is compiled as Javascript or Android binary HOT 1
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from language.