Values and types

Beginner30 min

Classify values by type, predict which mixed operations fail, and tell static from dynamic and strong from weak typing.

What you will learn
  • Classify a value as an integer, float, string, boolean or null
  • Predict whether an operation on mixed types works, fails or silently converts
  • Distinguish static from dynamic typing and strong from weak typing
On this page

Everything a program works with is a value, and every value has a type that decides what you can do with it. You can add two numbers but not two photographs, and you can capitalise a word but not a temperature. Types are how a language remembers which is which.

Values

A value is a piece of data: 42, 3.14, "hello", true. Written directly in code, it is a literal. A type is the kind of value, which determines how it is stored and which operations apply.

The basic types

Type What it holds Literals Notes
Integer Whole numbers 0, 42, -7 Exact. Some languages limit the size.
Float Numbers with a fractional part 3.14, -0.5, 2e3 Approximate; see the lesson on operators.
String Text "hello", "42" "42" is text, not a number.
Boolean True or false true, false Spelled True/False in Python.
Null The absence of a value None, null, NULL A value that means “nothing here”.

The string "42" and the integer 42 look alike on screen and behave nothing alike. One is two characters, the other is a quantity. Keeping them apart is the single most useful habit in this lesson.

Null deserves a note. It marks “no value”: a lookup that found nothing, an optional field left blank. It is not zero, and it is not an empty string. Its inventor, Tony Hoare, later called it a billion-dollar mistake because programs forget to check for it, so treat any value that might be null with suspicion.

Languages also differ in how many types they offer. JavaScript has a single number type for both whole and fractional values. Python, Java and C separate integers from floats. C goes further and has no string type at all: text is a run of single characters ending in a zero byte.

Seeing a value’s type

Every language lets you find out a value’s type, but each one goes about it differently, because the type lives in a different place. In Python and JavaScript it travels with the value, so you ask the value. In Java and C it is fixed by the declaration, so the program already knows it.

Python

types_demo.py
print(42, type(42).__name__)
print(3.14, type(3.14).__name__)
print("42", type("42").__name__)
print(True, type(True).__name__)
print(None, type(None).__name__)
print(2**100, type(2**100).__name__)
Output
42 int
3.14 float
42 str
True bool
None NoneType
1267650600228229401496703205376 int

Python’s int has no size limit, so 2**100 is still an int.

JavaScript

types_demo.js
console.log(42, typeof 42);
console.log(3.14, typeof 3.14);
console.log("42", typeof "42");
console.log(true, typeof true);
console.log(null, typeof null);
console.log(2 ** 100, typeof (2 ** 100));
Output
42 number
3.14 number
42 string
true boolean
null object
1.2676506002282294e+30 number

Whole and fractional values share one type, number. That typeof null says object is a well-known historical mistake in the language, kept for compatibility. And 2 ** 100 is only an approximation, as the exponent form hints.

Java

TypesDemo.java
import java.math.BigInteger;
public class TypesDemo {
static void show(Object value) {
System.out.println(value + " " + value.getClass().getSimpleName());
}
public static void main(String[] args) {
show(42);
show(3.14);
show("42");
show(true);
show('x');
show(BigInteger.TWO.pow(100));
}
}
Output
42 Integer
3.14 Double
42 String
true Boolean
x Character
1267650600228229401496703205376 BigInteger

The class names (Integer, Double) are the boxed forms of Java’s int and double. The built-in int stops at about 2.1 billion, so a huge value needs the library type BigInteger. null has no type of its own, which is why it is not in the list.

C

types_demo.c
#include <stdbool.h>
#include <stdio.h>
int main(void) {
int count = 42;
double ratio = 3.14;
char letter = '4';
bool done = true;
printf("%d int, %zu bytes\n", count, sizeof count);
printf("%.2f double, %zu bytes\n", ratio, sizeof ratio);
printf("%d char, %zu byte\n", letter, sizeof letter);
printf("%d bool, %zu byte\n", done, sizeof done);
printf("\"42\" is %zu bytes\n", sizeof "42");
return 0;
}
Output
42 int, 4 bytes
3.14 double, 8 bytes
52 char, 1 byte
1 bool, 1 byte
"42" is 3 bytes

The type is whatever you declared, and sizeof shows how much memory it takes. The character '4' is stored as the number 52, its character code. The text "42" takes 3 bytes: two characters and the zero byte that ends the text.

Mixing types

What happens when you combine different types depends on the language. Here is the same idea, the text "5" combined with the number 3.

Python

print("5" + 3)

Raises TypeError: can only concatenate str (not "int") to str. Python refuses to guess. Repeating text is defined, though: "5" * 3 gives '555'.

JavaScript

mix.js
console.log("5" + 3);
console.log("5" * 3);
Output
53
15

JavaScript converts silently. With + it treats the 3 as text and joins them; with * it treats the "5" as a number and multiplies.

Java

Mix.java
public class Mix {
public static void main(String[] args) {
System.out.println("5" + 3);
// int n = "5"; // compile error: String cannot be converted to int
}
}
Output
53

Java accepts the + because joining text and a number is a defined operation, but it will not assign a string to an integer variable, and reports that before the program runs.

C

mix.c
#include <stdio.h>
int main(void) {
printf("%d\n", '5' + 3);
return 0;
}
Output
56

C has no built-in string type. A character such as '5' is a small integer holding its character code (53), so adding 3 gives 56, not 8 and not "53". The language converts freely between character and integer types and does not object.

Static and dynamic typing

The question “when is the type checked?” splits languages in two.

Static typing Dynamic typing
Types belong to Variables and expressions Values
Checked Before the program runs (compile time) While it runs
Declared in code Usually yes (int count = 3;), sometimes inferred No (count = 3)
Wrong-type bug Reported before you run the program Reported only when that line executes
Examples Java, C, Rust, Go Python, JavaScript, Ruby
static: int count = "three"; -> rejected before anything runs
dynamic: count = "three" -> fine; later, count + 1 fails at that moment

Neither is better in general. Static typing catches whole classes of mistakes early and helps tools; dynamic typing is quicker to write and more flexible. Several dynamic languages now support optional type annotations that external tools check, a middle path you will see in the language courses.

Strong and weak typing

This is a separate question: how readily does the language convert between types behind your back?

  • Strong typing: few implicit conversions. Python rejects "5" + 3.
  • Weak typing: many implicit conversions. JavaScript turns "5" + 3 into "53" and "5" * 3 into 15.

The two axes are independent. Python is dynamic and strong. JavaScript is dynamic and weak. Java is static and strong. C is static, but weaker than Java because it converts freely between many numeric types and pointers.

Strong Weaker
Static Java, Rust C
Dynamic Python, Ruby JavaScript

Converting on purpose

When you want a conversion, ask for it explicitly. That is called casting or conversion. Reading a number from a user always gives you a string first, so you will convert constantly. Each program converts the text "42" to a number and the number 42 to text, then proves it by adding 1 to the number and appending ! to the text.

Python

convert.py
age = int("42")
text = str(42)
print(age + 1, text + "!")

JavaScript

convert.js
const age = Number("42");
const text = String(42);
console.log(age + 1, text + "!");

Java

Convert.java
public class Convert {
public static void main(String[] args) {
int age = Integer.parseInt("42");
String text = String.valueOf(42);
System.out.println((age + 1) + " " + text + "!");
}
}

C

convert.c
#include <stdio.h>
#include <stdlib.h>
int main(void) {
int age = atoi("42");
char text[12];
snprintf(text, sizeof text, "%d", 42);
printf("%d %s!\n", age + 1, text);
return 0;
}

All four print:

43 42!

A conversion can fail: "forty" has no sensible numeric answer. What happens next is one of the clearest differences between languages.

Language Converting "forty" to a number
Python int("forty") raises ValueError
JavaScript Number("forty") gives NaN (“not a number”) and carries on
Java Integer.parseInt("forty") throws NumberFormatException
C atoi("forty") silently returns 0

The first and third stop the program unless you handle the error, which the lesson on errors covers. The last two hand you a bad value that looks like a normal result, which is harder to notice.

Common pitfalls

  • Numbers that are really strings. Input from a keyboard, file or web form arrives as text. Sorting "10" and "9" as text puts "10" first.
  • Assuming silent conversion is harmless. "5" + 3 giving "53" is not a bug in JavaScript; it is a bug in your expectation.
  • Using null as a number or string. Calling a method on a null value is among the most common runtime failures in every language.
  • Confusing strong with static. Python has no declared types and is still strongly typed.
ExerciseSort the literals

Give the type of each literal: 7, 7.0, "7", "true", true, -0.25, "", null, 1e3, "3.14", 0, False, 'x', 100000000000, "None". Treat 'x' as text for this exercise.

Show solution
  • Integer: 7, 0, 100000000000
  • Float: 7.0, -0.25, 1e3 (the exponent form makes it a float)
  • String: "7", "true", "" (an empty string is still a string), "3.14", 'x', "None" (quotes make it text)
  • Boolean: true, False
  • Null: null

Anything inside quotes is a string, whatever it looks like. (In Java and C, single quotes make a one-character char instead, which is a number underneath.)

ExerciseWill it fail?

For each expression, say what Python does and what JavaScript does: (a) "10" - 4, (b) "10" + 4, (c) 10 + 4.5, (d) "ab" * 2. Write each as “result” or “error”.

Show solution

(a) Python: TypeError, since subtraction is not defined for strings. JavaScript: 6, because - forces a numeric conversion. (b) Python: TypeError. JavaScript: "104", joining as text. (c) Both give 14.5. Mixing an integer and a float is a safe, widely allowed widening. (d) Python gives "abab" (repetition is defined). JavaScript gives NaN, “not a number”, because "ab" cannot become a number.

ExerciseAdd two typed-in numbers

A program receives two pieces of text, "7" and "8", as if typed by a user. Convert them and print their sum, then print what you would get if you forgot to convert. Expected output: 15 for the sum. Without converting, text "7" joined to text "8" gives 78 in Python, JavaScript and Java.

Show solution

Python

add_typed.py
first = "7"
second = "8"
print(int(first) + int(second))
print(first + second)

JavaScript

add_typed.js
const first = "7";
const second = "8";
console.log(Number(first) + Number(second));
console.log(first + second);

Java

AddTyped.java
public class AddTyped {
public static void main(String[] args) {
String first = "7";
String second = "8";
System.out.println(Integer.parseInt(first) + Integer.parseInt(second));
System.out.println(first + second);
}
}

C

add_typed.c
#include <stdio.h>
#include <stdlib.h>
int main(void) {
const char *first = "7";
const char *second = "8";
printf("%d\n", atoi(first) + atoi(second));
printf("%s%s\n", first, second);
return 0;
}

C has no + for text, so the unconverted case is shown by printing the two strings one after the other.

All four print:

15
78

Summary

  • Every value has a type, and "42" (text) differs from 42 (number).
  • Static typing checks before running and dynamic typing checks while running; strong typing refuses implicit conversions and weak typing performs them.
  • Convert explicitly, and expect conversions to fail sometimes, in a different way in each language.

Next: Variables and assignment.