Dart Handbook for Typescript Developers
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
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