Skip to content

Add formal unchecked cast support #31142

Closed
@matanlurey

Description

@matanlurey

Sometimes the type system is not expressive enough, and especially in Dart 2.x, knowing the explicit static type is important (and in many cases, required - for example, invoking methods, accessors, etc).

Prior art in other nominal type-system languages:

(This seems free in most structural ones)

Language Support Notes
Swift No It has as!, but that is similar to Dart's as
Kotlin/Java No JVM has various unchecked modes, though.
C# No
C++ Yes, with reinterpret_cast

Current Support

There are three ways to "cast" that I know of:

Implicit Casts

void printSum(int a, int b) => print(a + b);

void foo(dynamic a, dynamic b) {
  printSum(a, b);
}

or

void foo(dynamic a) {
  TypeA actualA = a;
  a.doThing();
}

Explicit Casts

void foo(dynamic a) {
  var actualA = a as TypeA;
  a.doThing();
}

Type Promotion

void foo(dynamic a) {
  if (a is TypeA) {
    a.doThing();
  } else {
    throw new UnsupportedError('Not expected: $a');
  }
}

However, all of these fall short:

  • Implicit casts hide sources of (real) bugs in code:
List<String> firstThree(List<String> names) {
 return names.subList(0, 2);
}

void foo(List<String> allNames) {
  // RUNTIME ERROR: Iterable<String> is not List<String>
  var firstThree = firstThree(allNames.where((name) => name != 'Matan'));
}

... and is a warning with implicit-casts: false turned on.

  • Explicit casts, in both Dart2JS, DDC, and the Dart VM, have a cost:

Here is dart2js:

void main(dynamic input) {
  print((input as String).toUpperCase());
}
// Code shared by all dart2js compilations omitted.

main: [function(input) {
  P.print(H.stringTypeCast(input).toUpperCase());
}, "call$1", "main__main$closure", 2, 0, 7]
  • Type promotion forces awkward if-else handling code:

... as well as having a runtime cost for the is check.

String getBestProprtyOf(dynamic a) {
  if (a is Dog) {
    return a.tail.description;
  } else if (a is Cat) {
    return a.paws.description;
  } else {
    // Uh... I have to fill this in.
    throw new UnsupportedError('Neither a Cat or Dog');
  }
}

Proposal

Add a as! operator (or similar) - someone can think of a better syntax than me.

void foo(dynamic a) {
  var actualA = a as! _TypeIKnowForSure;
  print(actualA.propertyOnType);
}

Optionally, when assertions are enabled, I'd be OK for this use the normal as.

/cc @yjbanov @Hixie who I know have had similar requests.

Metadata

Metadata

Assignees

No one assigned

    Labels

    area-languageDart language related items (some items might be better tracked at github.com/dart-lang/language).

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions