Last updated:
0 purchases
none or
Provides a new class (None<T>) that masquarades as a Future<T> to enable
piggybacking on the only Union Type available in dart: FutureOr<T>. You can
then differentiate between an explicit null value and a default None<T>
value.
Unfortunately, the dart language lacks true Union Types and does not allow
for extending the capabilities of FutureOr<T> so this package deploys some
workarounds to use the NoneOr<T>? typedef as a standin paramter for a value
that may be T, null, or None (which is a new class that implements
Future but is in no way indended to actually behave like a future)
Sponsor #
Big thank you to our sponsor Scrapp Inc for supporting the development of this package!
Features #
This package introduces a new class None<T> which is a valid implementor of
Future<T> enabling it to be used in place of an argument or variable of type
FutureOr<T>. Included in the None type is the fallback static method that
can return T? taking from the primary field when primary is of type T? or
is null. However, it can return the optional fallback parameter if primary is
of type None<T> or Future<T>.
Getting started #
Add the package to your pubspec.yaml:
none_or: latest
copied to clipboard
Import the package with:
import 'package:none_or/none_or.dart';
copied to clipboard
See below for usage examples.
Usage #
The expected utility of this package is in allowing for differentiation between
explitly null values and values that are simply omitted. The below example
demonstrates how NoneOr<T> could be used to level-up a copyWith function for
a class with nullable fields:
import 'package:none_or/none_or.dart';
import 'package:flutter/foundation.dart';
// ...
/// A simple example immutable class with a copyWith function that uses
/// NoneOr<T> to ignore implicit no-change values given to a copyWith, while
/// respecting explicitly provided values including `null`
@immutable
class _NoneOrExampleClass {
final bool nonNullableField;
final bool? nullableField;
// ...
const _NoneOrExampleClass({
required this.nonNullableField,
this.nullableField,
// ...
});
@override
String toString() => 'Example($nonNullableField, $nullableField)';
/// Creates a copy of [this] with the given fields changed
_NoneOrExampleClass copyWith({
bool? nonNullableField,
NoneOr<bool>? nullableField = const None(),
// ...
}) =>
_NoneOrExampleClass(
nonNullableField: nonNullableField ?? this.nonNullableField,
nullableField: None.fallback(nullableField, this.nullableField),
// ...
);
/// Creates a copy of [this] with the given fields changed
_NoneOrExampleClass copyWithAlt({
// For consistency's sake, you could make every field NoneOr<T> even if
// the field is non-nullable. That way it would read the same from a
// logical perspective.
NoneOr<bool> nonNullableField = const None(),
NoneOr<bool>? nullableField = const None(),
// ...
}) =>
_NoneOrExampleClass(
nonNullableField: None.fallback(nonNullableField, this.nonNullableField),
nullableField: None.fallback(nullableField, this.nullableField),
// ...
);
_NoneOrExampleClass copyWithAlt2({
NoneOr<bool> nonNullableField = const None(),
// Note that NoneOr<bool?> is effectively the same as NoneOr<bool>?
// or even NoneOr<bool?>? - use whatever syntax most appeals to you.
// In the future, we may introduce a custom lint rule to dictate where
// we can put the "?" character
NoneOr<bool?>? nullableField = const None(),
// ...
}) =>
_NoneOrExampleClass(
nonNullableField: None.fallback(nonNullableField, this.nonNullableField),
nullableField: None.fallback(nullableField, this.nullableField),
// ...
);
}
// ...
void _exampleUsage() {
// baseline
_NoneOrExampleClass e0 = _NoneOrExampleClass(
nonNullableField: true,
nullableField: true,
); // Example(true, true)
// copies using copyWith:
// simple example with non-nullable underlying fields:
// 1. If field is ommitted, null is assumed, and existing value is kept (true)
_NoneOrExampleClass s1 = e0.copyWith(); // Example(true, true)
// 2. If field is marked null, existing value is kept (true)
_NoneOrExampleClass s2 = e0.copyWith(nonNullableField: null); // Example(true, true)
// 3. If field is given a value, value given overwrites existing (false)
_NoneOrExampleClass s3 = e0.copyWith(nonNullableField: false); // Example(false, true)
// simple example with nullable underlying fields:
// 1. If field is ommitted, None() is assumed, and existing value is kept (true)
_NoneOrExampleClass s1 = e0.copyWith(); // Example(true, true)
// 2. If field is marked null, existing value is overwritten with null (null)
_NoneOrExampleClass s2 = e0.copyWith(nonNullableField: null); // Example(true, null)
// 3. If field is given a value, value given overwrites existing (false)
_NoneOrExampleClass s3 = e0.copyWith(nonNullableField: false); // Example(true, false)
}
copied to clipboard
Performance #
Early testing suggests that this approach is less performant than alternate
approaches such as using a second boolean variable to set a field. Run on your
own system for comparison, but the rudimentary tests suggest that due to
the overhead of calling additional functions and comparing types, the approach
of using the second variable performs around ~100% faster than an approach
of manually testing for type in a ternary operator.
The methodology used to test was to run three versions of copyWith over
10,000,000 runs. In fairness, that is an unlikely impediment for casual use
copying a few values at a time, but if your usecase requires millions of
copies over a long time horizon, you may wish to consider the runtime
implications of this package's approach: this may save dev time and provide
convenience to developers at the expense of runtime optimizations.
See none_or_test.dart for the code
Additional information #
Right now there is no linter rule to differentiate the different ways of
using NoneOr<T> in your code. In the future, that may change. PRs are
always welcome.
Deprecation #
It is a sincere hope that at some future point this package will be rendered
obselete by future updates to the dart language. In the meantime, this is
meant as a stopgap to provide the necessary functionality into the dart
language in a way that aims to benefit from the type safety dart provides,
while enabling unintended behavior.
It is also possible that future updates to the dart language will further
block the workarounds this package uses in an attempt to crack down on this
workaround. Use at your own risk.
For personal and professional use. You cannot resell or redistribute these repositories in their original state.
There are no reviews.