How do I check whether a generic type is nullable in Dart NNBD? - dart

Let's say I had some function that takes a generic type as an argument. How do I check within that function whether the generic type argument is nullable or not? I want do something like this:
void func<T>() {
print(T is nullable);
}
void main(){
func<int>(); //prints false
func<int?>(); //prints true
}
All I can think of to do is to check if T.toString() ends with a ? which is very hacky.

Try:
bool isNullable<T>() => null is T;

The accepted answer really just checks if the type can be null. It doesn't care about the type that you are operating the null operator on.
If you want to check if a type is a specific nullable type, a.k.a if you want to check if a type is specifically one of type DateTime? and not String?, you can't do this in dart via T == DateTime? as this conflicts with ternary operator syntax.
However, since dart allows passing nullable types into generic arguments, it's possible to it like so:
bool isType<T, Y>() => T == Y;
isType<T, DateTime?>() works.

I have come across this a lot. And #Irn method works, except for when T is type Type (when using generics), it will always return saying that T is not null.
I needed to test the actual type of Type not Type its self.
This is what I have that is working really well for me.
bool get isNullable {
try {
// throws an exception if T is not nullable
final value = null as T;
return true;
} catch (_) {
return false;
}
}

It creates a new List instance to verify if it's type is nullable or not by using the is operator which supports inheritance:
bool isNullable<T>() => <T?>[] is List<T>;

Related

dart import to define <T>

I wish to check any list pass into checkList function. What import I should use for <T>. VS Code no suggest a Quick Fix for this.
The name 'T' isn't a type so it can't be used as a type argument.
Try correcting the name to an existing type, or defining a type named 'T'.
If you want to type safe you should do this
void checkList<T>(List<T> list){
// ...
}
If you want to be able to check any list with any contents, you should use:
void checkListObject(List<Object> list) {
// ...
}
If you want to enforce that the list has a concrete type, but you don't know what that is ahead of time, you can pass a type parameter to the function:
void checkListT<T>(List<T> list) {
// ...
}
This has the following behaviour:
checkListObject(["hello", 123]); // allowed
checkListObject([123, 234]); // allowed
checkListT(["hello", 123]); // allowed, T is Object
checkListT<String>(["hello", "world"]); // allowed, T is String
checkListT<String>([123, 234]); // not allowed, T is String but given List<int>
checkListT<String>([123, "hello"]); // not allowed, T is String but given List<Object>
You can use Object instead of T.
void checkList(List<Object> list){
}

How can I make sure that a non-nullable type will always be inferred?

I'm currently making an API that I want the optional value of type T to never be nullable (having null-safety enabled, of course).
Let's exemplify with the following:
void main() {
T nonNullableGeneric<T>(T value) {
if(value == null) {
throw 'This is null';
}
return value;
}
nonNullableGeneric(1); // Works fine
nonNullableGeneric(null); // Works fine BUT I would like that the type inference would not allow a nullable value
// Now this works as expected:
// "the argument type 'Null' can't be assigned to the parameter type 'int'."
//
// ... but I have to specify the type `T`, which is not clear given my function signature, that clearly
// requires a non-nullable value of type `T`
nonNullableGeneric<int>(null);
// How can I make the following call:
// nonNullableGeneric(null)
// to throw me something like "the argument type 'Null' can't be assigned to a parameter type 'T'."?
}
I've also created a dartpad.
As you can see, I must always specify the type if I want to make sure that it can't be Null when using a generic argument.
How can I make sure that it will never allow a nullable type?
I've found that the answer was really obvious, although I think it's reasonable to mention anyways: just make your type T constraints (extend) to a type of Object. This will make sure that that particular argument can never be optional.
Quoting the Object docs:
/// ...
/// Because `Object` is a root of the non-nullable Dart class hierarchy,
/// every other non-`Null` Dart class is a subclass of `Object`.
/// ...
So, in this example that I've mentioned, the solution would be:
void main() {
T nonNullableGeneric<T extends Object>(T value) {
return value;
}
nonNullableGeneric(1); // Works fine
// Throws a:
// Couldn't infer type parameter 'T'. Tried to infer 'Null' for 'T' which doesn't work: Type parameter 'T' declared to extend 'Object'.
// The type 'Null' was inferred from: Parameter 'value' declared as 'T' but argument is 'Null'.
// Consider passing explicit type argument(s) to the generic
nonNullableGeneric(null);
}

Why is `== null` preferable to `is Null`?

In Dart, checking for a value to be == null seems similar to checking if it is Null. Why is the former more preferable?
It is the type of comparison you are doing. In == null, you are comparing an object/primative to null whereas in the latter, is Null, null is an object. It makes no difference in the compiler.
== null is more favourable as the two being compared could be both primitives, which saves memory in the system.
== null is familiar to developers coming from other popular languages.
Using this type check in Dart not a quite correct.
if(value is Null) {
}
In Dart the static type of null is bottom type.
An as we know, the bottom type is a subtype of all types.
Now we test subtypes in Dart (including value of bottom type, null)
void main() {
var subtype = new Subtype();
var typeOfBase = new TypeOf<Base>();
test(subtype, typeOfBase);
test(null, typeOfBase);
}
void test(value, TypeOf typeOf) {
var type = typeOf.type;
var runtimeType = value.runtimeType;
if(typeOf.isSubtypeOf(value)) {
print("Value [$value] of type [$runtimeType] is a subtype of [$type[");
} else {
print("Value [$value] of type [$runtimeType] is NOT a subtype of [$type]");
}
}
class Base {
}
class Subtype implements Base {
String toString() => "subtype";
}
class TypeOf<T> {
Type get type => T;
bool isSubtypeOf(value) => value is T;
}
Results:
Value [subtype] of type [Subtype] is a subtype of [Base[
Value [null] of type [Null] is NOT a subtype of [Base]
So, here we can see that performing this test is not quite correct in Dart because null has not bottom type but a value of regular type Null which are not a bottom type and, of course, not a subtype of Base type.
So, I not recommend using is Null in Dart because in the future the class Null can be considered as deprecated and replaced by some internal implemented type as void or dynamic.

Syntax error when trying to determine if a variable is of a certain type

Pretty much as the title says: If you have a Type stored in a variable, there's no way to compare your actual object to this type variable, as far as I can tell. I can probably accomplish what I'm trying to do with mirrors, but I'd prefer not to if at all possible.
void example() {
Type myType = String;
String myExample = "Example";
//Syntax error here: The name 'myType' is not a type and cannot be used in an 'is' expression
if (myExample is myType) {
}
}
You can't generally test if a value is of a type using the Type object.
Type objects are reflected types, not real types. They represent the real type, but you can't use them in the code where you need a type: as type assertions, as generic type parameters or with the is/as operators. You must use the name of a type in those places, and not the name of a normal variable that happens to hold a Type object.
Clever stuff using mirrors might get there, but it's likely overkill for most cases (and I understand that you don't want it).
What you might be able to do instead, is to not pass around raw Type objects. You could instead make your own type abstraction, something like:
class MyType<T> {
const MyType();
Type get type => T;
bool isA(Object object) => object is T;
}
Then you can use that to represent types, not a Type object, and do something like:
void main(List<String> args) {
MyType myType = const MyType<String>();
String myExample = "Example";
if(myType.isA(myExample)) {
print('is');
} else {
print('is not');
}
}
That does require that your entire program uses your type objects to pass around types, but it also gives you a lot of control over those objects, so you can implement the functionality that you need.
I tried
library x;
void main(List<String> args) {
Type myType = String;
String myExample = "Example";
if(myExample.runtimeType == myType) {
print('is');
} else {
print('is not');
}
}
and it worked.
I have not much experience with such code in Dart though. Maybe that is not a fail-safe approach.
import 'package:reflection/reflection.dart';
void main() {
var childType = typeInfo(Child);
var baseType = typeInfo(Base);
if(childType.isA(baseType)) {
print("Child is Base");
}
if(baseType.isAssignableFrom(childType)) {
print("Base is assignable from Child");
}
}
class Base {
}
class Child extends Base {
}
Child is Base
Base is assignable for Child
P.S.
The "reflection" package incompatible with dart2js. It work only when used in Dart language.

How to prevent function return result declaratively?

Assume such conditions:
Some operation does not provide possibility of returning the result.
This operation declared as callback
Using typedef not recommended
Some operation provide of returning the result.
This operation declared as callback
Using typedef not recommended
Assume such scenario:
void main() {
executeVoidOperation(methodNonVoid); // Must throw if method void?
executeNonVoidOperation(methodVoid); // Must throw if method non-void?
}
int methodNonVoid() {
return 0;
}
void methodVoid() {
}
void executeVoidOperation(void operation()) {
operation(); // Must throw if method non-void?
}
void executeNonVoidOperation(dynamic operation()) {
var result = operation(); // Must throw if method void?
print(result); // Result of void operation? (if such passed as argument)
}
Displayed results:
null
Questions (where I wrong?):
Null is object. From where this null appeared (as result) if void function cannot return result (even null)?
Functions with different return types in Dart assumed as the same (not conflicting) types?
How in Dart called this function transformations?
executeNonVoidOperation(methodVoid); works because the callback is defined as dynamic operation(). dynamic can be anything, including void. It's the same as if you just don't specify a type.
The null value stems from a simple rule in Dart. Quoted from the Dart Language Tour:
All functions return a value. If no return value is specified, the statement return null; is implicitly appended to the function body.
That means that every void method always returns null. If you try to return something else, you'll get a runtime error (in checked mode).
executeVoidOperation(methodNonVoid); is a bit more tricky - I'd expect it to throw a runtime error, but it seems the callback is treated as dynamic operation() instead of void operation(). Dart Editor's analyzer seems to think that, too. This may be either a bug or a design choice by the Dart team.

Resources