Type System & Inference
Dart's Sound Type System
Dart has a sound static type system. "Sound" means the type system makes guarantees the compiler can verify — if the type checker says something is a String, it is always a String at runtime. No exceptions, no surprises.
Soundness was achieved in Dart 2.0 (2018). Before that, Dart had optional typing — a design the team ultimately abandoned for stronger safety guarantees.
Built-In Primitive Types
dart// Numbers int age = 25; // 64-bit integer double pi = 3.14159; // 64-bit floating point num flexible = 42; // supertype of int and double // Strings String name = 'JavaShark'; String multiLine = ''' This is multi-line '''; // Booleans bool isActive = true; // Collections (covered in depth later) List<int> scores = [95, 87, 92]; Map<String, int> grades = {'Alice': 95, 'Bob': 87}; Set<String> tags = {'dart', 'flutter'};
Type Inference with var and final
Dart infers types from the initializer — you don't always need to write the type:
dartvar name = 'JavaShark'; // inferred as String var count = 42; // inferred as int final ratio = 3.14; // inferred as double, cannot be reassigned // The type IS still checked — this is a compile error: // name = 123; // Error: A value of type 'int' can't be assigned to 'String'
dynamic vs Object? — The Escape Hatches
dart// dynamic: opts OUT of type checking entirely dynamic anything = 'hello'; anything = 42; // OK anything.foo(); // No compile error, but may throw at runtime! // Object?: the safe alternative — accepts any non-null value Object? safe = 'hello'; safe = 42; // OK // safe.length; // Compile error! Must cast first if (safe is String) { print(safe.length); // Smart cast — safe inside is block }
Prefer Object? over dynamic. It forces you to check the type before using it, keeping the type system sound.
Generics
Dart supports full reified generics — the type information is available at runtime:
dartclass Repository<T> { final List<T> _items = []; void add(T item) => _items.add(item); T get(int index) => _items[index]; List<T> getAll() => List.unmodifiable(_items); } final userRepo = Repository<User>(); userRepo.add(User('Alice', 30)); print(userRepo is Repository<User>); // true — generics are reified!
Summary
Dart's type system gives you the safety of a statically typed language with the ergonomics of a dynamically typed one. Type inference reduces boilerplate without sacrificing safety. Avoid dynamic — let the type system work for you.