Language: Typed Maps - like interfaces in TypeScript

Created on 16 Jan 2020  路  12Comments  路  Source: dart-lang/language

I would love to see typed Maps in Dart like it is possible in TypeScript.

TypeScript example:

interface IPerson {
    name: string;
    age: number;
    height?: number;
}

const person: IPerson = {
    name: "Max",
    age: 26
}

I wish Dart would support something like that so i do not have to create a class for it. It could look something like this:

interface IPerson {
  String name;
  int age;
  height? double;
}

Map<IPerson> person = {
  name: "Max",
  age: 26,
} 
feature

Most helpful comment

@vanesyan that's a different thing

Typed maps, aka structures/records, have a very different assignment behavior.

All 12 comments

You can already do it by using abstract class.

abstract class A {
  String get name;
  int get age;
  double? get height;
}

class B implements A {
  B({this.name, this.age, this.height});

  final int age;
  final String name;
  final double? height;
}

Still it is not possible to make map to implement abstract class as Map is just yet another class and thus { key: value } is just consice instantiation of that class.

@KoTTi97
If you want to construct instances in map like syntax without manually defining such constructor, then #698 might be a solution.

@vanesyan that's a different thing

Typed maps, aka structures/records, have a very different assignment behavior.

The "map" in TypeScript is different because it's a class, not a map. There's a Map class in TypeScript/JavaScript that is basically the same as maps in Dart. Plus, it's already possible to do what you want in Dart:

class Person {
  final String name;
  final int age;
  final double height;

  const Person({
    this.name,
    this.age,
    this.height,
  });
}

final Person person = Person(
  name: 'Max',
  age: 26
);

Introducing this basically means mixing two completely different concepts.

@KoTTi97 it's not maps you want, in typescript / javascript, these are called object literals. I would love to see this represented in dart as well via anonymous classes, the main use case for me is simpler json encodeing/decoding. In Go they are quite convenient as well as they remove a lot of boilerplate code that is created soley for sending / recieving responses.

car := struct {
    make    string
    model   string
    mileage int
}{
    make:    "Ford",
    model:   "Taurus",
    mileage: 200000,
}

Whats missing in dart is a way to declare a type inline as opposed to using a class declaration. The typescript example you showed doesn't really buy you anything when compared to what dart can already do, when this becomes useful is when declaring generic types, function return types and parameters, also variable types

const someFunc = (user: {name: string, age: number}): {data: {didSucceed: boolean } } => {
    const data: { didSucceed: boolean } = methodCall()
    return {data}
}

In dart you would have to define a class for every type that was defined inline here.

It would be nice to have something similar in dart, but - It will probably never happen - Typescript and Go are structurally typed,
and Dart isn't. C# has them, but they suffer from the same problems that dart does due to the type system and are only useful in the context of linq, they can't be used anywhere else.

I think Data classes are a great alternative and will alleviate most if not all of the pain in situations where anonymous objects would be used.

This looks something like Java's anonymous classes. In Dart, that would probably look like:

var foo = new Object() {
  int something = 42;
  String message = "Banana";
};

It won't give you any type for it, you'll still have to declare an interface:

abstract class Foo {
  abstract final int something;
  abstract final String message;
}
...
  var foo = const Foo() {
    final int something = 42; 
    final String message = "some text";
  };

Then foo will have a type which implements Foo.

Anonymous classes aren't the same thing imo.
Anonymous classes is about first-class citizen class.

Typed maps/structures/records are a mechanism separate from classes. They don't have constructors/inheritance.

@lrhn

I'm not sure that buys you anything over data classes for simple objects that are just containers for a set of fields with different types.

It might be useful if you are actually implementing an interface's methods though.

abstract class Foo {
  Future<bool> doSomething();
}

var foo = const Foo() {
   Future<bool> doSomething() async {
      return false;
 }
}

I can't really think of a use case for this off the top of my head, but F# has them so I guess they are a useful somehow?

Seeing as how Dart has a nominal type system, I'm not sure how anonymous objects with structural equality could be supported (aside from some from of runtime reflection maybe? ), but for the op - If/when Dart lands support for Type Aliases and Sum Types , you can (almost) solve the same set of problems in a different way, E.G.

enum SomeUnionType {
    OneType,
    AnotherType,
    LastType,
}
typedef MyType = Map<String, SomeUnionType>;

MyType  myInstance =  { "key1": OneType(), "key2": AnotherType(), "key3": LastType() };

// destructuring based on key name would help also if this feature is added. 
var { key1, key2, key3 } = myInstance; 

Dart has structural types too: Function types, FutureOr and (with Null Safety) nullable types. It's not being non-nominal that's the biggest challenge, it's having a type which is not assignable to Object?. The structural types that we have are still backed by actual objects.

I'm not a language designer, so I am not so sure about the implementation. In typescript, the type doesn't exist at runtime anyway so they can just do whatever. For Go, I'm not sure - the struct cant be assigned to interface{} unless it's a pointer. In Scala the type is assignable to Object, but then runtime reflection is used for field access, which obviously won't work for dart.

I miss this too.
The point is that the interface should not exist so it would be basically a desugar/analyzer thing:

interface Foo {
  String bar;
}

// example 1
var baz = <String, dynamic>{
  'bar': 'hi'
};
var foo = baz as Foo;

print(foo.bar); // prints hi
// the above would be basically a de-sugar to print(foo['bar']);

// example 2
var baz = Foo()..bar = 'hello';
// desugering to
var baz = <String, dynamic>{
  'bar': 'hello'
};

I guess this _kinda_ exists when you use JS interop:

@JS()
@anonymous
class Foo {
  external String get bar;
  external set bar(String n);

  external factory Foo({String bar});
}

var foo = Foo(bar: 'hi');
// I guess the above line transpiles to something like in JS:
let foo = {
  'bar': 'hi'
};
Was this page helpful?
0 / 5 - 0 ratings

Related issues

listepo picture listepo  路  3Comments

kevmoo picture kevmoo  路  3Comments

natebosch picture natebosch  路  4Comments

moneer-muntazah picture moneer-muntazah  路  3Comments

har79 picture har79  路  5Comments