第131期:Dart类型和类型推断

1,424 阅读5分钟

封面图

image.png

用ts定义了一些类,用来实现前端监控系统。

Dart类型系统

Dart使用了静态类型检查运行时检查组合,用于确保变量的类型始终保持一定的正确性。虽然Dart是强类型语言,但是由于存在类型推断,所以在声明变量时,也不一定非得指定变量的类型,类型推断会自动推断变量的类型。

ps: 这一点和和ts非常相似。

静态类型检查的一个好处是能够让Dart的静态分析器(static analyzer)在编译时发现相应的错误。

我们可以通过向泛型类添加类型注释来修复大多数静态分析错误。最常见的泛型类是集合类型List<T>Map<K,V>

比如:下面的例子中printInts()方法打印的是一个int类型的数值列表,main()方法创建了一个列表,传给printInts()进行打印:

void printInts(List<int> a) => print(a);

void main() {
  final list = [];
  list.add(1);
  list.add('2');
  printInts(list); // static analysis : error /warning
}

这时候会出现一个类型错误的报错。

error - The argument type 'List<dynamic>' can't be assigned to the parameter type 'List<int>'. - argument_type_not_assignable

dynamic类型不能赋值给int类型。

其实这是因为中间存在一个隐式转换

list在初始化时没有指定类型,因而分析器没有足够的信息来推断比dynamic类型更合适的类型,但是打印方法需要int类型才能正确执行,因而导致类型的不匹配。

当我们给list指定int类型后,同时向list中添加正确的数值,则可以避免这个问题。

void printInts(List<int> a) => print(a);

void main() {
  final list = <int>[];
  list.add(1);
  list.add(2);
  printInts(list); // success
}

Soundness 完整性? 强制性?健全性?强类型语言?

我们都知道TS是一种强类型语言,也知道它也有静态检查和运行时检查,可以帮助我们在代码编译时发现代码中的错误。

但是,有这种检查的功能就是强类型语言吗?这种理解也许稍微有点牵强,不够深刻。

强类型,健全性表现在它可以确保我们的程序不会进入到某种无效的状态。这种无效状态指的是表达式计算值表达式静态类型不匹配的状态。比如:假如我们的表达式静态类型是string,那么在代码运行的时候,我们可以保证计算这个字符串时变量获得的必然是字符串。

强类型的好处

强类型的优点我们都知道:

  • 可以在编译时提示与类型相关的错误信息,避免运行时出现错误。
  • 可以提高代码的可读性,其他成员可以很方便的获取代码的相关信息,不用来会翻代码来猜测某些对象中有哪些属性。
  • 可读性好,那么代码的可维护性必然会提高很多。
  • 更好的预(AOT)编译。虽然AOT编译在没有类型的情况下是可能的,但生成的代码效率要低得多。

类型推断

类型推断这个词我们都知道,也知道TS中也存在类型推断,但是类型推断的过程是什么?很少有人能说明白。

在Dart中,分析器(analyzer)可以推断字段、方法、局部变量和大多数泛型类型参数的类型。当分析器没有足够的信息来推断特定的类型时,它会使用动态类型dynamic type

比如:我们定义一个名为arguments的变量,它是一个map类型。

Map<String, dynamic> arguments = {'argA': 'hello', 'argB': 42};

当然我们也用var定义这个变量,然后Darth会对类型进行推断:

var arguments = {'argA': 'hello', 'argB': 42}; // Map<String, Object>

Map会推断它的每个条目argA,argB,然后根据条目去推断它们对应的类型。

比如上面的map中,map的键argA,argB都是字符串,但是它们的值一个是string,一个是int,但是它们的上层对象都是Object,所以整体会被推断为Map<String, Object>

字段,属性,方法的推断

没有指定类型并且从父类中重写的字段或方法,或者方法继承自父类的方法或字段的类型,这些类型会继承父类中对应的类型。

没有指定类型或者没有使用继承的变量,但是进行了初始化,这种类型推断会根据初始值进行。

比如:

var a = 'hello world'

初始值是字符串,则后面会推断为string类型。

即,推断变量的类型取决于知道该变量的类型

局部变量推断

局部变量推断也是根据变量的初始值进行推断,并且不考虑后续的赋值操作。比如:

var x = 3; // x is inferred as an int.
x = 4.0; // static analysis: error /warning

x根据其初始值被推断为int类型,后面赋值为double类型,则会提示错误。

当我们初始化时指定其类型后则不会报错:

num y = 3; // A num can be double or int.
y = 4.0; // success

最后

先写到这里。

简单理解一下强类型语言的稍微深刻一点的认识,同时简单理解一下类型推断到底是怎么进行的推断。

文笔有限,多谢~

公众号《Javascript高级程序设计》,文章会同步到公众号。