Dart Handbook for Typescript Developers

Lee young-jun
10 min readOct 14, 2023

--

Before diving into Flutter development, I made a habit of studying on my way home after work every day.

In this article, I’ll provide a concise summary of the Dart programming language. I won’t cover the details of setting up Dart

Playground

Typescript

https://www.typescriptlang.org/play

Dart

https://dartpad.dev/?

Variables

Typescript

let stringValueWithType: string
let stringValue = '123'
let nullableValue: string | null = null
let uninitializedValue: string

Dart

String stringValueWithType;
var stringValue = '123';
String? nullableValue; // = null

String uninitializedValue;
print(uninitializedValue)
// syntax error
// The non-nullable local variable 'uninitializedValue' must be assigned before it can be used.

late String uninitializedValue;
print(uninitializedValue)
// runtime errorr
// Late variable 'uninitializedValue' without initializer is definitely unassigned

Constants

Typescript

const constValue = '123'

Dart

const constValue = '123'

var value = 123
final finalValue = value; // readonly
const constValue = value // error, allows only known(constant) values

Operators

is

Typescript

const isString = (v: any): v is string => {
return typeof v === 'string';
}

Dart

var stringValue = '123';
var isString = stringValue is String;

is!

Dart

var numberValue = 123;

var isNotString = numberValue is! String;
print(isNotString);

..

Dart

var person = Person()
..name = 'leesam'
..age = 20
..address = (Address()
..country = 'korea'
..street = 'my street'
)

same

var person = Person()
person.name = 'leesam'
person.age = 20

?..

Dart

var person = NullablePerson() // 
?..name = 'leesam'
..age = 20

Decorator & Annotation

Typescript

const Todo = (who: string, what: string) => {
return (
originalMethod: Function,
_context: ClassMethodDecoratorContext
) => {
function ReplacementMethod(this: any, ...args: any[]) {
console.log('who', who, 'what', what)
const result = originalMethod.call(this, ...args);
return result;
}
return ReplacementMethod;
};
}

class Person {
@Todo('Dash', 'Implement this function')
doa() {
console.log('Do something');
}
}

Dart

import 'dart:mirrors'; // unsupported import: dart:mirrors

class Todo {
final String who;
final String what;

const Todo(this.who, this.what);
}

@Todo('Dash', 'Implement this function')
void Person() {
print('Do something');
}

void main() {
final hasAnnotation = Person.runtimeType.toString().contains("Todo");
final reflectedPerson = reflectClass(Person);
final todo = classMirror.metadata.first.reflectee as Todo;
print(todo.who);
}

Typescript

const array = [1,2,3,4]
const clone = [...array]

Dart

var array = [1,2,3,4]
final clone = [...array]

int[]? nullableArray
final clone = [...array]

Types

Number

Typescript

let value: number

Dart

int intValue;
double doubleValue;

String

Typescript

const multilineValue = "first" + 
"second" +
"third";

Dart

var multilineValue = 'first'
"second"
'third';

var multilineValue = """first
second
third""";

Code in String

Typescript

let value = 123
const stringValue = `${value}`

Dart

var value = 123;
final stringValue = '${value}';

Tuple

Typescript

let values: [number, string] = [1, '123']
const [first, second] = values

Dart

var values = (1, 'last');
var (first, second) = values;

Record

Typescript

let record : { first: number, second: string } = {
first: 1,
second: '2'
}

record = { first: 2, second: '3' }

Dart

({ int first, String second }) record;
record = (first: 1, second: '2');

print(record.$1);
//== print(record.first);
(int, String) record;
record = (1, '2');

print(record.$1); // not .$0
print(record.$2);

Map

Typescript

let map : Record<string, number> = {
first: 1,
second: 2
}

let map : Record<string, any> = {
...
}

Dart

final map = <String, int>{
'first': 1,
'second': 2,
'third': 3,
};

final map = <String, any>{
...
}

List

Tyescript

let isTest = false

let list = isTest ? [1, 2, 3, 4] : [1, 2, 3];

Dart

var isTest = false
var list = [1, 2, 3, if(isTest) 4];
var list = <int>[]

Set

Typescript

let record = new Set([1,2,3,4,1])
record.add(1)

console.log(record.size)

Dart

var numbers = [1, 2, 3, 4, 5];
var sets = {1, 2, 3, 4, 5};
sets.add(1);
sets.addAll(numbers);

print(sets.length);

Generic

Interface

Typescript

interface IPerson<C> {
create(): C
}

Dart

interface class IPerson<C> {
C create();
}

Class

Typescript

class Person<C> {
create(): C {
return {} as C
}
}

let person = new Person<Number>()
let createdNumber = person.create()

Dart

class Person<C> {
C create() {
return {} as C;
}
}

...

var person = new Person();
person.create();

Extends

Typescript

class Person<C extends String> {
create(): C {
return {} as C
}
}

let person = new Person<String>()
let createdString = person.create()

Dart

class Person<C extends String> {
C create() {
return {} as C;
}
}

Method

Typescript

class Person {
create<C extends String>(c: C): C {
return {} as C
}
}

Dart

class Person {
C create<C extends String>() {
return {} as C;
}
}

Private

Typescript

class Person {
private key: string;
...
}

Dart

class Person {
private string _key;
...
}

Typedef

Typescript

type NumberList = number[]
type LIST<T> = T[]

Dart

typedef IntList = List<int>;
typedef LIST<T> = List<T>;

Override

Typescript

abstract class IPerson<C> {
create(): C {
return {} as C
}
}

class Person<C> extends IPerson<C> {
create(): C {
return super.create()
}
}

Dart

abstract class IPerson<C> {
C create();
}

class Person<C> extends IPerson<C> {
@override
C create() {
return {} as C;
}
}

Switch

Typescript

const a = 'a'
const b = 'b'
let obj = [a, b]
const [_a , _b] = obj

if (_a === a && _b === b)
console.log(`${x1}, ${x2}`);

Dart

const a = 'a';
const b = 'b';
var obj = ['a', 'b'];
switch (obj) {
// List pattern [a, b] matches obj first if obj is a list with two fields,
// then if its fields match the constant subpatterns 'a' and 'b'.
case [a, b]:
print('$a, $b');
}

double calculateArea(Shape shape) => switch (shape) {
Square(length: var l) => l * l,
Circle(radius: var r) => math.pi * r * r
};

For loop

Typescript

const list = [1,2,3,4]

for(let i in list)
console.log(i);

const [_, a, __] = list

Dart

const list = [1,2,3,4];

for(var i in list)
print(i);

var [_, a, _] = list

Destruct

Typescript

class Person {
title: string
age: number

constructor(name: string, age: number) {
this.title = name
this.age = age
}
}

let person = new Person('leesam', 20)
const { title, age } = person

Dart

class Person {
String name;
int age;

Person(this.name, this.age);
}

var person = Person('leesam', 20);
var Person(:name, :age) = person;

Function

Named Parameter

Typescript

type enableFlagsParams = {
bold?: boolean
hidden?: boolean
}

type IEnableFlags = (params: enableFlagsParams) => void

const enableFlags: IEnableFlags = ({bold, hidden}) =>
{
...
}

enableFlags({
bold: true,
hidden: false
});

Dart

void enableFlags({bool? bold, bool? hidden}) {...}
...

enableFlags(bold: true, hidden: false);

Required Parameter

Typescript

type enableFlagsParams = {
bold: boolean
hidden: boolean
}

enableFlags({
bold: true,
//hidden: false -- requires hidden
});

Dart

void enableFlags({bool? bold, required bool? hidden}) {...}
...

enableFlags(bold: true); // The named parameter 'hidden' is required, but there's no corresponding argument

Optional

Typescript

function say(from: string, msg: string, device?: string): string {
var result = '$from says $msg'
if (device != null) {
result = `${result} with a ${device}`
}
return result
}

say('korea', 'hi')

Dart

String say(String from, String msg, [String? device]) {
var result = '$from says $msg';
if (device != null) {
result = '$result with a $device';
}
return result;
}

say('korea', 'hi');

Anonymous

Typescript

const list = [1,2,3,4]
const newList = list.map(item => item.toString())

Dart

final list = [1,2,3,4];
var newList = list.map((item) {
return item.hashCode;
});

Async

Typescript

const test = async (): Promise<number>  => {
return 0
}

...

await test()

Dart

Future<int> test() async {
return 0;
}

...

int result = await test();

Exception

Typescript

try {
breedMoreLlamas();
} catch (e) {
if (e instanceof OutOfLlamasException) {
buyMoreLlamas();
} else if (e instanceof Exception) {
print(`Unknown exception: ${e}`);
} else {
print(`Something really unknown: ${e}`);
}
}

Dart

try {
breedMoreLlamas();
} on OutOfLlamasException {
buyMoreLlamas();
} on Exception catch (e) {
print('Unknown exception: $e');
} catch (e) {
print('Something really unknown: $e');
}

Constructor

Typescript

class Point {
x: number = 0;
y: number = 0;

constructor(x: number, y: number) {
this.x = x;
this.y = y;
}
}

Dart

class Point {
double x = 0;
double y = 0;

Point(double x, double y) {
this.x = x;
this.y = y;
}

Point(this.x, this.y);

Point.zero()
: x = 0,
y = 0 {
print('zero point created');
}

Point.zero() : this(0, 0);

Point.fromJson(Map json)
: x = json['x'],
y = json['y'] {
print('created from json $x, $y');
}

const Point(this.x, this.y);
}

Super

Typescript

class Person {
firstName?: string;

static fromJson(data: Map<string, any>): Person {
console.log('in Person');
return new Person()
}
}

class Employee extends Person {
// Person does not have a default constructor;
// you must call super.fromJson().
static fromJson(data: Map<string, any>) {
super.fromJson(data)

console.log('in Employee');
}
}

Dart

class Person {
String? firstName;

Person.fromJson(Map data) {
print('in Person');
}
}

class Employee extends Person {
// Person does not have a default constructor;
// you must call super.fromJson().
Employee.fromJson(super.data) : super.fromJson() {
print('in Employee');
}
}

Singleton

Tyepescript

class Person {
firstName?: string;

static fromJson(data: Map<string, any>): Person {
console.log('in Person');
return new Person()
}

static _unique: Person | null = null
static unique(): Person {
if (!this._unique) this._unique = Person.fromJson({})

return this._unique
}
}

Dart

class Person {
String? firstName;

Person.fromJson(Map data) {
print('in Person');
}

static late Person? _unique = null;

factory Person.unique() {
if (_unique == null)
_unique = Person.fromJson({});

return _unique!;
}
}

Property

Typescript

class Rectangle {
left: number = 0
top: number = 0
width: number = 0
height: number = 0

constructor(left: number, top: number, width: number, height: number) {
this.left = left
this.top = top
this.width = width
this.height = height
}

get right(): number { return this.left + this.width }
set right(value: number) { this.left = value - this.width }
get bottom(): number {return this.top + this.height }
set bottom(value: number) { this.top = value - this.height }
}

let rect = new Rectangle(3, 4, 20, 15);
rect.left === 3
rect.right = 12;

Dart

class Rectangle {
double left, top, width, height;

Rectangle(this.left, this.top, this.width, this.height);

double get right => left + width;
set right(double value) => left = value - width;
double get bottom => top + height;
set bottom(double value) => top = value - height;
}

...

var rect = Rectangle(3, 4, 20, 15);
assert(rect.left == 3);
rect.right = 12;

noSuchMethod

Typescript

class Person {
name = ""
}

let obj: any = new Person()

obj.test() // obj.test is not a function

Dart

class Person {
String? firstName;

@override
void noSuchMethod(Invocation invocation) {
print('You tried to use a non-existent member: '
'${invocation.memberName}');
}
}

...

dynamic person = Person();
person.test(); // You tried to use a non-existent member: Symbol("test")

Mixin

Mixin

Dart

mixin Runner {
bool isRunning = false;

void run() {
this.isRunning = true;
}
}

with

Dart

class Person with Runner {
String? firstName;
}

on

Dart

mixin Runner on Person {
bool isRunning = false;

void run() {
this.isRunning = true;
}
}

class Person {

}

class RunnablePerson extends Person with Runner {

}

class Beast {

}

// Error: 'Beast' doesn't implement 'Person' so it can't be used with 'Runner'.
class RunnableBeast extends Beast with Runner {

}

Enum

Typescript

enum Vehicle {
car = 1,
}

Dart

enum Vehicle implements Comparable<Vehicle> {
car(tires: 4, passengers: 5, carbonPerKilometer: 400),
bus(tires: 6, passengers: 50, carbonPerKilometer: 800),
bicycle(tires: 2, passengers: 1, carbonPerKilometer: 0);

const Vehicle({
required this.tires,
required this.passengers,
required this.carbonPerKilometer,
});

final int tires;
final int passengers;
final int carbonPerKilometer;

int get carbonFootprint => (carbonPerKilometer / passengers).round();

bool get isTwoWheeled => this == Vehicle.bicycle;

@override
int compareTo(Vehicle other) => carbonFootprint - other.carbonFootprint;
}

String enum

Typescript

enum Vehicle {
car = "car",
}

Dart

enum Vehicle {
car(1, 'car');

const Vehicle(this.id, this.value);

final int id;
final String value;
}

Extension Method

Method

Typescript

declare global {
export interface Array<T> {
first(where?: (element: T) => boolean): T | null
}
}

Array.prototype.first = function (where: (element: Element) => boolean) {
if (this.length <= 0) return null
if (!where) return this[0]

return this.find(e => where(e)) || null
}

Dart

extension ListExtension<E> on List<E> {
E? firstItem(bool Function(E) where) {
for (var element in this) {
if (where(element)) {
return element;
}
}
return null;
}
}

// extension on List<E> { possible

...

List<int> numbers = [1, 2, 3, 4, 5];
int? firstEven = numbers.firstItem((element) => element.isEven);

dynamic dnumbers = [1, 2, 3, 4, 5];
// error
int? firstDEven = dnumbers.firstItem((element) => element.isEven);

Conflict

Dart

import 'list_lib.dart'; // has firstItem
import 'list_lib2.dart' hide ListExtension;
// import '{package}' hide {extension name};

Callable

Dart

class WannabeFunction {
String call(String a, String b, String c) => '$a $b $c!';
}

var wf = WannabeFunction();
var out = wf('Hi', 'there,', 'gang');

Base

Dart

// Library a.dart
base class Vehicle {
void moveForward(int meters) {
// ...
}
}

// b.dart
import 'a.dart';

// Can be constructed
Vehicle myVehicle = Vehicle();

// Can be extended
base class Car extends Vehicle {
int passengers = 4;
// ...
}

// ERROR: Cannot be implemented
base class MockVehicle implements Vehicle {
@override
void moveForward() {
// ...
}
}

sealed

Dart

sealed class Vehicle {}

class Car extends Vehicle {} // can extends

class Truck implements Vehicle {} // can implements

Vehicle myVehicle = Vehicle(); // can not create instance

Vehicle myCar = Car(); // can create sub-class instance

Stream

Dart

Future<int> sumStream(Stream<int> stream) async {
var sum = 0;
await for (final value in stream) {
sum += value;
}
return sum;
}

Isolate

run

Typescript

import { runOnJS } from 'react-native-reanimated';

function App() {
// While on the UI thread
runOnJS(navigation.goBack)();
}

Dart

void main() async {
// Read some data.
final jsonData = await Isolate.run(_readAndParseJson);

// Use that data.
print('Number of JSON keys: ${jsonData.length}');

// closure
final jsonData = await Isolate.run(() async {
final fileData = await File(filename).readAsString();
final jsonData = jsonDecode(fileData) as Map<String, dynamic>;
return jsonData;
});
}

Future<Map<String, dynamic>> _readAndParseJson() async {
final fileData = await File(filename).readAsString();
final jsonData = jsonDecode(fileData) as Map<String, dynamic>;
return jsonData;
}

spawn(group)

Dart

import 'dart:isolate';

void isolateFunction(SendPort sendPort) {
// This function will run in a separate Isolate.
// It sends a message back to the main Isolate.
sendPort.send("Hello from the Isolate!");
}

void main() async {
// Create a ReceivePort in the main Isolate to receive messages.
ReceivePort receivePort = ReceivePort();

// Spawn an Isolate and run the isolateFunction.
Isolate isolate = await Isolate.spawn(isolateFunction, receivePort.sendPort);

// Listen for messages from the Isolate in the main Isolate.
receivePort.listen((message) {
// Received message from the Isolate.
print("Received message from Isolate: $message");
receivePort.close();
isolate.kill(); // Terminate the Isolate.
});

print("Main Isolate: Working on something else...");

// Continue with other tasks in the main Isolate.
for (int i = 0; i < 5; i++) {
print("Main Isolate: Working on task $i");
await Future.delayed(Duration(seconds: 1));
}
}

https://dart.dev/language/concurrency

References

https://dart.dev/language/variables

--

--

No responses yet