Type Annotations in TypeScript

A type annotation tells TypeScript exactly what type a variable, parameter, or return value should be. Learn the syntax, where annotations go, and when to use them.

6 min read

Type annotations in TypeScript are the : type syntax you write after a variable, function parameter, or function signature. An annotation tells the compiler exactly what type a value should be, and the compiler rejects any assignment that does not match.

Here is the smallest useful example:

typescripttypescript
let message: string = "Hello, TypeScript!";
 
console.log(message.toUpperCase());

The colon and string after message set a contract: this variable must always hold text. Try assigning it a number instead, and TypeScript refuses to compile the file until you fix the mismatch.

typescripttypescript
let message: string = "Hello, TypeScript!";
 
message = 42;

TypeScript compares the new value against the original annotation on every assignment, not just the first one. Because 42 is a number and message was declared as a string, the compiler stops here and reports the exact mismatch before the code ever runs.

texttext
Type 'number' is not assignable to type 'string'.

Plain JavaScript would accept that reassignment silently and only fail later, possibly deep inside a function that expected a string. This is the core value of annotations: they turn a mistake into a compile-time error instead of a runtime surprise.

Where Annotations Go

Type annotations appear in three main positions, and each one protects a different part of your code.

Variable Annotations

Place the annotation after the variable name:

typescripttypescript
let count: number = 0;
let name: string = "Alice";
let isReady: boolean = false;

Each annotation sets a contract for that variable, exactly like the message example above. TypeScript checks every future assignment against it and rejects anything that does not match, whether that happens on the next line or two hundred lines later.

Function Parameter Annotations

Place the annotation after each parameter name:

typescripttypescript
function greet(name: string, age: number) {
  return `${name} is ${age} years old.`;
}
 
console.log(greet("Alice", 30));

Parameters are the most important place to add annotations. TypeScript has no value to look at when a function is declared, so unlike variables, it cannot infer a parameter's type on its own. Without an annotation, a parameter falls back to any and loses all type safety, which means the compiler stops catching mistakes at that call site entirely.

typescripttypescript
greet("Alice", "thirty");

Because age is annotated as a number, passing a string in that position is a type mismatch. TypeScript flags this call before the code runs, right where the mistake happens, rather than letting it become a bug you have to trace later.

texttext
Argument of type 'string' is not assignable to parameter of type 'number'.

Return Type Annotations

A return type annotation protects the output side of a function instead of its inputs. Place it after the parameter list, before the function body:

typescripttypescript
function add(a: number, b: number): number {
  return a + b;
}

A return type annotation does two things: it documents what callers should expect, and it forces the compiler to check that every path through the function body actually returns that type. The function below forgets to return a value when the condition is false.

typescripttypescript
function getScore(): number {
  if (Math.random() > 0.5) {
    return 100;
  }
}

Because the return type is annotated as a number, TypeScript checks every code path and notices that the false branch falls through without returning anything at all. It reports the missing return immediately instead of letting the bug surface later as undefined in production.

texttext
Function lacks ending return statement and return type does not include 'undefined'.

Without the return type annotation, TypeScript would have silently widened the inferred type instead of raising an error, and the missing return could slip through unnoticed. For more on how TypeScript figures out types without annotations, see type inference in TypeScript.

Common Annotation Types

Annotations are not limited to the three primitives shown above. The same colon-and-type pattern works for arrays, objects, and unions of multiple types:

typescripttypescript
let scores: number[] = [85, 92, 78];
 
let user: { name: string; age: number } = {
  name: "Alice",
  age: 30,
};
 
let id: string | number = "abc123";

scores only accepts an array of numbers, user requires both a name and an age with the listed types, and id accepts either a string or a number because of the union. The syntax stays identical no matter how complex the type gets: a colon, then the type.

When to Annotate and When to Skip

You do not need to annotate every variable. TypeScript infers types from the initial value in most cases, so writing an annotation on top of an obvious value just adds noise:

typescripttypescript
let title = "Learn TypeScript"; // TypeScript infers string
let count = 0;                  // TypeScript infers number

Annotations are most useful in these situations:

  • Function parameters: TypeScript cannot infer them, so you must annotate.
  • Function return types: they catch mistakes inside the function and document intent.
  • Variables declared without an initial value: TypeScript cannot infer a type with nothing to look at.
  • When you want a narrower type than inference gives you: a union instead of a wider primitive, for example.

For a deeper look at when inference is enough and when you should add annotations, read when to add type annotations in TypeScript.

Common Mistakes

Forgetting Parameter Annotations

The most common beginner mistake is skipping annotations on function parameters:

typescripttypescript
function greet(name, age) {
  return `${name} is ${age}`;
}

Without annotations, both name and age become any, so the function accepts anything you pass it, including values that would crash the template string. Enabling strict mode in your compiler configuration turns this into a hard error instead of a silent risk.

Annotating Variables That Do Not Need It

typescripttypescript
let color: string = "blue";
let items: number[] = [1, 2, 3];

TypeScript already knows the type of each variable from its initial value, so these annotations repeat information the compiler already has. Removing them keeps the code shorter without losing any safety.

Forgetting That Annotations Are Compile-Time Only

typescripttypescript
let value: number = 42;
 
console.log(typeof value); // "number"

Type annotations do not exist once the code runs. You cannot inspect an annotation with typeof, and it has zero effect on execution; after compilation the annotation is gone, leaving plain JavaScript behind with no trace of the type. This distinction between compile-time types and runtime values comes up often, especially when working with APIs, user input, and validation.

Rune AI

Rune AI

Key Insights

  • A type annotation uses : type syntax after a variable, parameter, or function signature.
  • Annotations are removed during compilation and have zero runtime effect.
  • Function parameters always need annotations because TypeScript cannot infer them.
  • Return type annotations catch mistakes inside the function body, not just at call sites.
  • Let TypeScript infer types where it can, and annotate where inference is not enough.
RunePowered by Rune AI

Frequently Asked Questions

Do type annotations affect runtime performance?

No. Type annotations are erased during compilation. The JavaScript output is identical to hand-written JavaScript, so there is zero runtime overhead.

Do I need to annotate every variable?

No. TypeScript can infer types in many cases, especially when you initialize a variable with a value. You mainly need annotations on function parameters and when a variable starts without a value.

What happens if I annotate a variable with the wrong type?

The TypeScript compiler reports an error before your code runs. The error tells you the actual type does not match the annotation, so you can fix it immediately.

Conclusion

Type annotations are the core syntax that makes TypeScript useful. By writing : type after a variable, parameter, or function, you tell the compiler what to expect. Annotations catch mistakes at compile time, document your intent for teammates, and power editor features like autocomplete and refactoring. Start with annotations on function parameters and return types, let inference handle the rest, and add more annotations as your codebase grows.