Core Fundamentals
At its core, a program manipulates data. A variable is a name that refers to a location in memory where you store data. A data type is a classification that tells the compiler or interpreter how the programmer intends to use the data. It defines the kind of values a variable can hold and the operations that can be performed on it.
For example, if you have a variable age of type Integer, the system knows it can perform arithmetic operations on it, like addition or subtraction. It also knows it can't (or shouldn't) perform string operations like concatenation without an explicit conversion.
A type system is a set of rules that a programming language uses to assign and enforce data types. The primary goal of a type system is to prevent type errors—errors that occur when an operation is performed on a data type it wasn't designed for (e.g., adding a number to a string without explicit instruction).
Type systems are generally classified along two independent axes:
This is about when the language checks the types.
In a statically-typed language, type checking is done at compile-time. The developer must explicitly declare the data type of a variable before using it. This means errors are caught early in the development cycle, often before the program is even run.
// Java: Must declare the type 'String'
String message = "Hello, World!";
int number = 10;
// number = "hello"; // Compile-time error: Type mismatch
// C++: Must declare the type 'std::string'
#include <string>
std::string message = "Hello, World!";
int number = 10;
// number = "hello"; // Compile-time error
// TypeScript: Explicitly typed
let message: string = "Hello, World!";
let number: number = 10;
// number = "hello"; // Compile-time error
In a dynamically-typed language, type checking is done at run-time. You don't need to declare a variable's data type. The type is inferred at the moment it's assigned a value.
# Python: Type is inferred at runtime
message = "Hello, World!" # message is a string
message = 10 # Now it's an integer. No problem.
// JavaScript: Type is inferred at runtime
let message = "Hello, World!"; // message is a string
message = 10; // Now it's a number. Perfectly fine.
# Ruby: Type is inferred at runtime
message = "Hello, World!" # message is a String
message = 10 # Now it's an Integer.
This is about how strictly the language enforces its type rules. It's a spectrum, not a binary choice.
In a strongly-typed language, once a variable has a type, it's "stuck" with it. The language is very strict about how you can mix and match types. If you want to perform an operation between two different types, you must perform an explicit conversion.
# Python: Strongly typed
result = 10 + "5"
# TypeError: unsupported operand type(s) for +: 'int' and 'str'
# You must explicitly convert:
result = 10 + int("5") # result is 15
// Java: Strongly typed
// int result = 10 + "5"; // Compile-time error
// You must explicitly convert, though Java has specific rules for string concatenation
String result = 10 + "5"; // This is a special case, result is "105"
In a weakly-typed language, the rules are more relaxed. The language will try to "guess" what you mean when you mix types, performing an implicit conversion. This can be convenient, but it can also lead to unexpected behavior and subtle bugs.
// JavaScript: Weakly typed
let result = 10 + "5";
// JavaScript implicitly converts 10 to "10"
// result is "105" (a string), not 15. This can be a source of bugs.
// PHP: Weakly typed
$result = 10 + "5";
// PHP implicitly converts "5" to an integer 5
// $result is 15 (an integer)
$result2 = 10 . "5"; // The '.' is for string concatenation
// $result2 is "105" (a string)
Here's how popular languages fit into this matrix. Note that the strong/weak axis is a continuum, and the placement is a subject of debate.
| Strong Typing | Weak Typing | |
|---|---|---|
| Static Typing | Java, C++, C#, Rust, Go, Swift, TypeScript | C (in some aspects) |
| Dynamic Typing | Python, Ruby | JavaScript, PHP |
Understanding type systems is crucial for an intermediate engineer because it demonstrates a deeper understanding of how a language works under the hood.
105 instead of 15 in JavaScript, you'll immediately suspect implicit type coercion.