Does Dart have an equivalent of TypeScript's --noImplicitAny, enforcing static typing unless explicitly disabled? - flutter

I'm getting to grips with Flutter and the Dart language, but keep occasionally making silly mistakes because the language lets me. In the docs on type inference, it says:
The analyzer can infer types for fields, methods, local variables, and most generic type arguments. When the analyzer doesn’t have enough information to infer a specific type, it uses the dynamic type.
This seems to happen a fair amount, and there's no way to see what the analyzer was and wasn't able to infer. For example:
void doThings(List<int> integers) {
print(integers);
}
main() {
doThings([1, 2, 3].map((i) => i + 1));
}
There's no warning in the IDE (IntelliJ) or during compilation, but at runtime we get an ugly TypeError: Instance of 'MappedListIterable<int, int>': type 'MappedListIterable<int, int>' is not a subtype of type 'List<int>' because I needed a .toList() call.
To catch mistakes like that at compile time instead of runtime, TypeScript has an option --noImplicitAny which makes any un-inferrable type into an error, rather than silently substituting a variant type (dynamic in Dart, any in TypeScript).
Is there a way to make the Dart compiler this helpful too?

You can customise the static analyzer. You need to add analysis_options.yaml to your root.
To forbid implicit types use the following:
analyzer:
strong-mode:
implicit-casts: false
implicit-dynamic: false
More information: https://dart.dev/guides/language/analysis-options

Related

Why bother casting the return value since the type has been specified when calling the function?

I am learning Editor Script of Unity recently, and I come across a piece of code in Unity Manual like this:
EditorWindowTest window = (EditorWindowTest)EditorWindow.GetWindow(typeof(EditorWindowTest), true, "My Empty Window");
I don't know why bother casting the result with (EditorWindowTest) again since the type has been specified in the parameter field of GetWindow().
Thanks in advance :)
There are multiple overloads of the EditorWindow.GetWindow method.
The one used in your code snippet is one of the non-generic ones. It accepts a Type argument which it can use at runtime to create the right type of window. However, since it doesn't use generics, it's not possible to know the type of the window at compile time, so the method just returns an EditorWindow, as that's the best it can do.
You can hover over a method in your IDE to see the return type of any method for yourself.
When using one of the generic overloads of the GetWindow method, you don't need to do any manual casting, since the method already knows at compile time the exact type of the window and returns an instance of that type directly.
The generic variants should be used when possible, because it makes the code safer by removing the need for casting at runtime, which could cause exceptions.
If you closely look, GetWindow's return type is EditorWindow. Not the EditorWindowTest, so typecasting makes sense.
https://docs.unity3d.com/ScriptReference/EditorWindow.GetWindow.html

How do I use Type Aliases / Typedefs (also non-function) in Dart?

I have known function typedefs in Dart for a long time. They are also explained in answers to this question.
Now, I have heard about non-function type aliases (or non-function typedefs) coming to Dart.
I am wondering two things:
What exactly are (non-function) typedefs in Dart?
How do I use them (in my Flutter project)?
Generalized type aliases / typedefs in Dart
You can view the feature specification for Generalized type alisases for the full design document.
I want to preface this by pointing out that Dart used to only support typedefs for functions. The new generalized feature supports typedefs for any type.
typedef JsonMap = Map<String, dynamic>;
JsonMap parseJsonMap(String input) => json.decode(input) as JsonMap;
This is especially useful when you have multiple generic types (type parameters) that cause long type names that are tedious to type, for example Map<ScaffoldFeatureController<SnackBar, SnackBarClosedReason>, SnackBar>. This can now be simplified using a type alias:
typedef ScaffoldSnackBarMap = Map<ScaffoldFeatureController<SnackBar, SnackBarClosedReason>, SnackBar>;
Syntax
If not clear from the above examples, this is the syntax for type alisases / typedefs:
'typedef' identifier typeParameters? '=' type ';'
This means that you always need to start with the typedef keyword followed by your desired identifier, e.g. FooTypeDef. After that, you can add type parameters, e.g. Foo<K, V>. The last step is adding the = symbol followed by the actual type you want to create an alias for. This can be any type, i.e. a class, primitive type, function type, or w/e. Do not forget the ; at the end ;)
// Type parameters / generic types in typedef.
typedef Foo<K, V> = Map<K, V>;
// Type alias for regular types.
typedef Bar = Widget;
// As well as primitive types.
typedef Baz = int;
// Function types are also supported.
typedef FooFunction<T, R> = R Function(T param);
Deprecating names
Additionally, you can use typedefs for any class names. Say you want to rename your class from Provider to Pod because you think the former is too verbose. If you are maintaining a package, this would be a breaking change. With the new generalized type aliases, you can simply rename your class and create a type alias that you deprecate:
class NewClassName<T> {}
#Deprecated("Use NewClassName instead")
typedef OldClassName<T> = NewClassName<T>;
Note that this example and the one above are taken from the proposed CHANGELOG entry for the feature.
How to use them
The feature will be shipped by default with Dart 2.13 but is currently still experimental. I will cover how to use it in both ways; the experimental method can be removed later on.
Dart 2.13
As I mentioned previously, the feature will be enabled by default starting with Dart 2.13. If you currently have Dart 2.13 installed already (you can use dart --version to check it for example), you can use this method. Otherwise, you should refer to the Experimental support section below.
In your pubspec.yaml, you need to define the lower bound on your Dart SDK constraint to be greater than or equal to 2.13.0:
environment:
dart: '>=2.13.0 <3.0.0'
Experimental support
In your Flutter project (or any other Dart project), you currently need to enable them as an experiment. It means that they are hidden behind a feature flag.
Experimental Dart features can be configured using analysis_options.yaml. You can simply create an analysis_options.yaml file in the root of your project directory and add the following lines:
analyzer:
enable-experiment:
- nonfunction-type-aliases
Now, you need to also enable the experiment when you run (or build) your app:
flutter run --enable-experiment=nonfunction-type-aliases
To make sure that you can use this feature, use the master channel (flutter channel master when using Flutter).

dart + flutter: why doesn't the compiler recognize type mismatches

I'm encapsulating the data of my flutter app inside a class called AppData. It looks a bit like this:
class AppData with ChangeNotifier{
List<word.Word> _words;
UnmodifiableListView<word.Word> words;
AppData(){
// at some point this will be loaded from disk
// for now I'm just using a hardcoded list of words
_words = word.words;
// the following code would work:
// words = UnmodifiableListView(_words);
// this doesn't, but it's also not a compiler error !?
words = _words;
}
// ...
}
The AppData class keeps track of a list of words. For the consumers of AppData, this is visible as an UnModifiableListView.
My code had a very obvious bug: I assigned _words to words without encapsulating the List in the UnModifiableListView properly.
Why is this not found by the compiler?
The types should be an obvious mismatch. From the dart docs (emphasis mine):
Dart’s type system, like the type systems in Java and C#, is sound. It
enforces that soundness using a combination of static checking
(compile-time errors) and runtime checks. For example, assigning a
String to int is a compile-time error. Casting an Object to a string
using as String fails with a runtime error if the object isn’t a
string.
Update, to respond to Rémi's answer:
The error message is:
The following assertion was thrown building MultiProvider:
type 'List<Word>' is not a subtype of type 'UnmodifiableListView<Word>'
This seems like an issue of Covariance vs Contravariance.
If I know that my reference to List actually contains an UnmodifiableListView, then I could do the cast myself.
Why would the compiler add an implicit cast for me?
It seems to me as if this bypasses a lot of the type soundness mentioned in the docs above. Especially when I change my type hierarchies and do extensive refactoring, I rely on the compiler to tell me: You've got the types mixed up.
It's very possible that their inheritance tree still reaches a common ancestor at some point. But they are definitely not the same.
At least to me, it is even more surprising, since this is not the way other "typical" OOP languages work (Java, C#, ...).
So I still wonder: Why doesn't the compiler find this, what is the reason behind this design?
What happens, in reality, is an implicit cast.
Since UnmodifiableListView is a List, assigning List to UnmodifiableListView does an automatic cast.
You can disable that on the analysis_options.yaml like so:
analyzer:
strong-mode:
implicit-casts: false

In Scala, why does a type annotation must follow for the function parameters ? Why does the compiler not infer the function parameter types?

I have a simple question, why is the Scala compiler not able to infer the function parameter types by itself ?
Some functional programming languages, like Haskell, can infer almost
all types, because they can perform global type inference. Scala can’t
do this, in part because Scala has to support subtype polymorphism
(inheritance), which makes type inference much harder. Here is a
summary of the rules for when explicit type annotations are required
in Scala.
When Explicit Type Annotations Are Required
In practical terms, you have to provide explicit type annotations for the following situations:
A mutable var or immutable val declaration where you don’t assign a value, (e.g., abstract declarations in a class like val book: String, var count: Int
All method parameters (e.g., def deposit(amount: Money) = {…}).
Method return types in the following cases:
1)When you explicitly call return in a method (even at the end).
2)When a method is recursive.
3)When two or more methods are overloaded (have the same name) and one of them calls another; the calling method needs a return type annotation.
4)When the inferred return type would be more general than you intended, e.g., Any.
Source Programming Scala, 2nd Edition - O'Reilly Media
I have a simple question, why is the Scala compiler not able to infer the function parameter types by itself ?
Scala has local type inference. If you look at the method, and look at what information is there locally, it should be easy to see, that there simply is no information about what the types could possibly be.
Say, you have the following method:
def foo(a) = a + a
How is Scala going to figure out what type a is? It can only figure that out from looking at how the method is called, but that is non-local information!
And even worse: since Scala supports dynamic code loading, it is possible that the calling code doesn't even exist yet at compile time.

Benefit of explicitly providing the method return type or variable type in scala

This question may be very silly, but I am a little confused which is the best way to do in scala.
In scala, compiler does the type inference and assign the most closest(or may be Restrictive) type for each variable or a method.
I am new to scala, and from many sample code/ libraries, I have noticed that in many places people are not explicitly providing the types for most of the time. But, in most of the code I wrote, I was/still am explicitly providing the types. For eg:
val someVal: String = "def"
def getMeResult() : List[String]= {
val list:List[String] = List("abc","def")
list
}
The reason I started to write this especially for method return type is that, when I write a method itself, I know what it should return. So If I explicitly provide the return type, I can find out if I am making any mistakes. Also, I felt it is easier to understand what that method returns by reading the return type itself. Otherwise, I will have to check what the return type of the last statement.
So my questions/doubts are :
1. Does it take less compilation time since the compiler doesn't have to infer much? Or it doesn't matter much ?
2. What is the normal standard in the scala world?
From "Scala in Depth" chapter 4.5:
For a human reading a nontrivial method implementation, infering the
return type can be troubling. It’s best to explicitly document and
enforce return types in public APIs.
From "Programming in Scala" chapter 2:
Sometimes the Scala compiler will require you to specify the result
type of a function. If the function is recursive, for example, you
must explicitly specify the function’s result type.
It is often a good idea to indicate function result types explicitly.
Such type annotations can make the code easier to read, because the
reader need not study the function body to figure out the inferred
result type.
From "Scala in Action" chapter 2.2.3:
It’s a good practice to specify the return type for the users of the
library. If you think it’s not clear from the function what its return
type is, either try to improve the name or specify the return type.
From "Programming Scala" chapter 1:
Recursive functions are one exception where the execution scope
extends beyond the scope of the body, so the return type must be
declared.
For simple functions perhaps it’s not that important to show it
explicitly. However, sometimes the inferred type won’t be what’s
expected. Explicit return types provide useful documentation for the
reader. I recommend adding return types, especially in public APIs.
You have to provide explicit return types in the following cases:
When you explicitly call return in a method.
When a method is recursive.
When two or more methods are overloaded and one of them calls another; the calling method needs a return type annotation.
When the inferred return type would be more general than you intended, e.g., Any.
Another reason which has not yet been mentioned in the other answers is the following. You probably know that it is a good idea to program to an interface, not an implementation.
In the case of return values of functions or methods, that means that you don't want users of the function or method to know what specific implementation of some interface (or trait) the function returns - that's an implementation detail you want to hide.
If you write a method like this:
trait Example
class ExampleImpl1 extends Example { ... }
class ExampleImpl2 extends Example { ... }
def example() = new ExampleImpl1
then the return type of the method will be inferred to be ExampleImpl1 - so, it is exposing the fact that it is returning a specific implementation of trait Example. You can use an explicit return type to hide this:
def example(): Example = new ExampleImpl1
The standard rule is to use explicit types for API (in order to specify the type precisely and as a guard against refactoring) and also for implicits (especially because implicits without an explicit type may be ignored if the definition site is after the use site).
To the first question, type inference can be a significant tax, but that is balanced against the ease of both writing and reading expressions.
In the example, the type on the local list is not even a "better java." It's just visual clutter.
However, it should be easy to read the inferred type. Occasionally, I have to fire up the IDE just to tell me what is inferred.
By implication, methods should be short enough so that it's easy to scan for the result type.
Sorry for the lack of references. Maybe someone else will step forward; the topic is frequent on MLs and SO.
2. The scala style guide says
Use type inference where possible, but put clarity first, and favour explicitness in public APIs.
You should almost never annotate the type of a private field or a local variable, as their type will usually be immediately evident in their value:
private val name = "Daniel"
However, you may wish to still display the type where the assigned value has a complex or non-obvious form.
All public methods should have explicit type annotations. Type inference may break encapsulation in these cases, because it depends on internal method and class details. Without an explicit type, a change to the internals of a method or val could alter the public API of the class without warning, potentially breaking client code. Explicit type annotations can also help to improve compile times.
The twitter scala style guide says of method return types:
While Scala allows these to be omitted, such annotations provide good documentation: this is especially important for public methods. Where a method is not exposed and its return type obvious, omit them.
I think there's a broad consensus that explicit types should be used for public APIs, and shouldn't be used for most local variable declarations. When to use explicit types for "internal" methods is less clear-cut and more a matter of judgement; different organizations have different standards.
1. Type inference doesn't seem to visibly affect compilation time for the line where the inference happens (aside from a few rare cases with implicits which are basically compiler bugs) - after all, the compiler still has to check the type, which is pretty much the same calculation it would use to infer it. But if a method return type is inferred then anything using that method has to be recompiled when that method changes.
So inferring a method (or public variable) that's used in many places can slow down compilation (particularly if you're using incremental compilation). But inferring local or private variables, private methods, or public methods that are only used in one or two places, makes no (significant) difference.