Types & Operations
Written by Jonathan Sande
Heads up... You're reading this book for free, with parts of this chapter shown beyond this point astext.
Life is full of variety, and that variety expresses itself in different ways. What type of toothpaste do you use? Spearmint? Cinnamon? What’s your blood type? A? B? O+? What type of ice cream do you like? Vanilla? Strawberry? Praline pecan fudge swirl? Having names for all of these different things helps you talk intelligently about them. It also helps you to recognize when something is out of place. After all, no one brushes their teeth with praline pecan fudge swirl. Though it does sound kind of nice.
Programming types are just as useful as real-life types. They help you to categorize all the different kinds of data you use in your code.
In Chapter 2, “Expressions, Variables & Constants”, you learned how to name data using variables and also got a brief introduction to Dart data types. In this chapter, you’ll learn even more about types and what you can do with them.
Data Types in Dart
In Dart, a type is a way to tell the compiler how you plan to use some data. By this point in this book, you’ve already seen the following types:
The last one in that list,
String, is the type used for text like
Just as you don’t brush your teeth with ice cream, Dart types keep you from trying to do silly things like dividing text or removing whitespace from a number.
Dart has even more built-in types than just the ones listed above. The basic ones, such as
num will serve you adequately in a great variety of programming scenarios, but when working on projects with specific needs, it’ll be convenient to create custom types instead. A weather app, for example, may need a
Weather type, while a social media app may need a
User type. You’ll learn how to create your own types in Chapter 5, “Control Flow”, and Chapter 8, “Classes”.
As you learned in Chapter 2, “Expressions, Variables & Constants”, types like
num are subclasses, or subtypes, of the
Object defines a few core operations, such as testing for equality and describing itself. Every non-nullable type in Dart is a subtype of
Object, and as a subtype, shares
Object’s basic functionality.
Note: You’ll learn about nullable types in Chapter 11, “Nullability”.
In the previous chapter, you also got a sneak peek at type inference, which you’ll look at in more depth now.
Annotating Variables Explicitly
It’s fine to always explicitly add the type annotation when you declare a variable. This means writing the data type before the variable name.
int myInteger = 10; double myDouble = 3.14;
Creating Constant Variables
Declaring variables the way you did above makes them mutable. If you want to make them immutable, but still keep the type annotation, you can add
final in front.
const int myInteger = 10; const double myDouble = 3.14;
final int myInteger = 10; final double myDouble = 3.14;
Letting the Compiler Infer the Type
While it’s permissible to include the type annotation as in the example above, it’s redundant. You’re smart enough to know that
10 is an
3.14 is a
double, and it turns out the Dart compiler can deduce this as well. The compiler doesn’t need you to explicitly tell it the type every time — it can figure the type out on its own through a process called type inference. Not all programming languages have type inference, but Dart does — and it’s a key component behind Dart’s power as a language.
const myInteger = 10; const myDouble = 3.14;
Checking the Inferred Type in VS Code
Sometimes, it can be useful to check the inferred type of a variable or constant. You can do this in VS Code by hovering your mouse pointer over the variable name. VS Code will display a popover like this:
Checking the Type at Runtime
Your code can’t hover a mouse pointer over a variable to check the type, but Dart does have a programmatic way of doing nearly the same thing: the
num myNumber = 3.14; print(myNumber is double); print(myNumber is int);
Sometimes, you’ll have data in one type, but need to convert it to another. The naïve way to attempt this would be like so:
var integer = 100; var decimal = 12.5; integer = decimal;
A value of type 'double' can't be assigned to a variable of type 'int'.
integer = decimal.toInt();
Operators With Mixed Types
So far, you’ve only seen operators acting independently on integers or doubles. But what if you have an integer that you want to multiply with a double?
const hourlyRate = 19.5; const hoursWorked = 10; const totalCost = hourlyRate * hoursWorked;
const totalCost = (hourlyRate * hoursWorked).toInt();
Const variables must be initialized with a constant value.
final totalCost = (hourlyRate * hoursWorked).toInt();
Ensuring a Certain Type
Sometimes you want to define a constant or variable and ensure it remains a certain type, even though what you’re assigning to it is of a different type. You saw earlier how you can convert from one type to another. For example, consider the following:
const wantADouble = 3;
final actuallyDouble = 3.toDouble();
const double actuallyDouble = 3;
const wantADouble = 3.0;
The image below shows a tree of the types you’ve encountered so far.
Object is a supertype of
num is a supertype of
double are subtypes of
num, which is a subtype of
num someNumber = 3;
The getter 'isEven' isn't defined for the type 'num'.
final someInt = someNumber as int; print(someInt.isEven);
num someNumber = 3; final someDouble = someNumber as double;
_CastError (type 'int' is not a subtype of type 'double' in type cast)
final someDouble = someNumber.toDouble();
- Create a constant called
age1and set it equal to
42. Create another constant called
age2and set it equal to
21. Check that the type for both constants has been inferred correctly as
intby hovering your mouse pointer over the variable names in VS Code.
- Create a constant called
averageAgeand set it equal to the average of
age2using the operation
(age1 + age2) / 2. Hover your mouse pointer over
averageAgeto check the type. Then check the result of
averageAge. Why is it a
doubleif the components are all
Object and dynamic Types
var myVariable = 42; myVariable = "hello";
var answer = myVariable * 3; // runtime error
var myVariable = 42; myVariable = 'hello'; // compile-time error
dynamic myVariable = 42; myVariable = 'hello'; // OK
var myVariable; // defaults to dynamic myVariable = 42; // OK myVariable = 'hello'; // OK
Object? myVariable = 42; myVariable = 'hello'; // OK
Before moving on, here are some challenges to test your knowledge of types and operations. It’s best if you try to solve them yourself, but solutions are available with the supplementary materials for this book if you get stuck.
Challenge 1: Teacher’s Grading
You’re a teacher, and in your class, attendance is worth 20% of the grade, the homework is worth 30% and the exam is worth 50%. Your student got 90 points for her attendance, 80 points for her homework and 94 points on her exam. Calculate her grade as an integer percentage rounded down.
Challenge 2: What Type?
What’s the type of
const value = 10 / 2;
- Type conversion allows you to convert values of one type into another.
- When doing operations with basic arithmetic operators (
/) and mixed types, the result will be a
- Type inference allows you to omit the type when Dart can figure it out.
- Dart is an optionally-typed language. While it’s preferable to choose statically-typed variables, you may write Dart code in a dynamically-typed way by explicitly adding the
dynamictype annotation in front of variables.