The first thing we will start with is understanding that TypeScript is smart. You don’t have to explain the obvious to it.
Type Inference
Many devs (like me) coming from strongly typed languages (like older C, Java, or C#) are naturally accustomed to the idea of typing everything.
// ❌ Redundant (Unnecessary Boilerplate)
const userName: string = "Alice";
const userAge: number = 41;
const isAdmin: boolean = true;// ❌ Redundant (Unnecessary Boilerplate)
const userName: string = "Alice";
const userAge: number = 41;
const isAdmin: boolean = true;TypeScript already knows that “Alice” is a string. You don’t have to swear it.
// ✅ Perfect (Clean and readable)
const userName = "Alice"; // TS infers: string (or literal "Alice", we'll see this later)
const userAge = 41; // TS infers: number
const isAdmin = true; // TS infers: boolean// ✅ Perfect (Clean and readable)
const userName = "Alice"; // TS infers: string (or literal "Alice", we'll see this later)
const userAge = 41; // TS infers: number
const isAdmin = true; // TS infers: booleanThis is called Type Inference. You let TS deduce the type from the initial value.
const vs let: The Key Difference
This is where things get interesting and where TS shines.
With let (Mutable Variables)
When you use let, TS assumes that you are going to change the value, so it infers the “general” primitive type.
// TS understands this as: "Ah, 'status' is a STRING and it will change to any other string".
let status = "pending";
status = "success"; // ✅ Good
status = "error"; // ✅ Good
status = 123; // ❌ If you try this it will return an error.
// You cannot put a number into a string.// TS understands this as: "Ah, 'status' is a STRING and it will change to any other string".
let status = "pending";
status = "success"; // ✅ Good
status = "error"; // ✅ Good
status = 123; // ❌ If you try this it will return an error.
// You cannot put a number into a string.With const (Constants)
When you use const, TS knows that this value will never change. So it can be much more specific.
const method = "GET";
// TS says: "This is not just a string. It is THE string 'GET'".
// The literal type is "GET".
const method = "GET";
// TS says: "This is not just a string. It is THE string 'GET'".
// The literal type is "GET".
This seems silly now, but it is the basis for understanding why sometimes TS complains that "GET" is not assignable to string (or vice versa) in complex configurations.
When TO annotate types
So, if TS infers it, is it never necessary to place types? No, there are cases where it is necessary:
- When inference fails or is too broad:
From my point of view with little experience, I would let TS infer any (or put it myself) in cases where I don’t know what is going to happen, whether due to errors or because the response can be very broad.
// TS infers 'any' if not configured in 'strict' mode (which forces you to always annotate types)
let data;
data = fetchSomething(); // You can pass it whatever you want and TS won't complain.// TS infers 'any' if not configured in 'strict' mode (which forces you to always annotate types)
let data;
data = fetchSomething(); // You can pass it whatever you want and TS won't complain.But be careful with any, because you are stripping TS of all its purpose. Ideally, you shouldn’t be forced to use it.
- In function parameters (Here it is mandatory!):
// ❌ TS cannot read your mind nor know who will call this
function greet(person) {
...
}
// ✅ Here you define the contract
function greet(person: string) {
...
}// ❌ TS cannot read your mind nor know who will call this
function greet(person) {
...
}
// ✅ Here you define the contract
function greet(person: string) {
...
}- To define explicit return contracts: Sometimes you want to ensure that your function returns X, even if it infers Y.
Summary of Part 1
- Less is more: If TS can infer it, don’t write it. Your code will be cleaner.
- Variable (
let) = General Type (string,number). - Constant (
const) = Literal Type (The exact value).
In Part 2, we will look at how to use those literal types to our favor with Unions, and how to tell TS: “This variable can only be ‘admin’ or ‘user’, nothing else”.
Experiment!
Here you have a real TypeScript editor running in your browser to test what you learned.