Basic Types & Type System
๐ค Basic Types & Type System
The Primitive Types
TypeScript includes seven primitive types from JavaScript, plus two special types:
string
Text values. Can use single quotes, double quotes, or backticks.
const name: string = "Alice"; const greeting: string = 'Hello'; const message: string = `Hi, ${name}`;
number
All numeric values: integers, floats, Infinity, NaN.
const count: number = 42; const price: number = 19.99; const infinity: number = Infinity;
boolean
Only two values: true or false.
const isActive: boolean = true; const hasError: boolean = false;
null & undefined
Special values representing absence. Usually used with unions.
const x: null = null; const y: undefined = undefined;
bigint
Numbers larger than Number.MAX_SAFE_INTEGER (rare).
const huge: bigint = 9007199254740992n;
symbol
Unique identifiers (advanced; rarely used in beginner code).
const id: symbol = Symbol("id");
๐ Type Annotations
const age: number = 25; const name: string = "Bob"; const isStudent: boolean = true; // โ TypeScript error! number is not assignable to string const city: string = 42;
Annotations on Function Parameters & Returns
You can annotate parameters and specify what a function returns:
function multiply(x: number, y: number): number { return x * y; } // โ Works multiply(3, 4); // Returns 12 // โ Error! Argument of type 'string' is not assignable to parameter of type 'number' multiply("3", 4);
๐ง Type Inference
const count = 42; // TypeScript knows count is a number const name = "Alice"; // TypeScript knows name is a string const active = true; // TypeScript knows active is a boolean
However, inference has limits. Sometimes you need explicit annotations:
const data = null; // TypeScript infers type 'null', might not be what you want function process(value) { // No annotation, value's type is unclear return value.toUpperCase(); // Error: TypeScript doesn't know what value is }
Best Practice: When to Annotate
| Situation | Recommendation |
|---|---|
Clear from assignment (e.g., const x = 5) |
Skip annotation, let inference work |
| Function parameters | Always annotate |
| Function return values | Highly recommended (documents intent) |
| Complex/unclear types | Always annotate for clarity |
Variable initialized to null or undefined |
Always annotate (inference too vague) |
๐ญ Special Types: any & unknown
The any Type
any is an "escape hatch" that disables type checking. Use it sparingly!
let data: any = "hello"; data = 42; // โ Fine data = true; // โ Fine data.toUpperCase(); // โ Fine (no error, even though data might be a number!)
Why avoid `any`? It defeats the entire purpose of TypeScript. You lose type safety and IDE support.
The unknown Type
unknown is the safer, stricter cousin of any. It's type-safe because it forces you to check what the value actually is before using it.
let data: unknown = "hello"; data = 42; // โ Fine // โ Error! Object is of type 'unknown' data.toUpperCase(); // โ But this is fine โ we checked first! if (typeof data === "string") { data.toUpperCase(); // Now TypeScript knows data is a string }
any vs unknown
unknown instead of any. Always. unknown requires you to check the type before using the value, which keeps your code safe.
๐ Type Narrowing (Preview)
As we saw with `unknown`, TypeScript can narrow types based on your code. This happens with:
- typeof checks:
typeof x === "string" - Instanceof checks:
x instanceof Date - Truthiness checks:
if (x) { ... } - Equality checks:
if (x === null) { ... }
We'll dive deep into type narrowing in a later chapter, but understanding it now will help you write safer TypeScript.
๐ป Coding Challenges
Challenge 1: Type Annotation Errors
Fix the type errors in this code by adding correct type annotations:
const age = "25"; const score = 98.5; function greet(person) { return "Hello, " + person; }
Goal: Add type annotations so each variable/parameter has the correct type.
Challenge 2: Inference vs Annotation
Which of these variables need explicit type annotations, and which can rely on inference?
const x = 42; const y = null; function process(value) { ... } const isReady = true;
Goal: Identify which ones need annotations and explain why.
Challenge 3: any vs unknown
Write a function that safely handles unknown input. Check if it's a number, and if so, double it. Otherwise, return 0.
function doubleIfNumber(value: unknown): number { // Your code here }
Goal: Demonstrate type narrowing with `unknown`.
While you can let TypeScript infer types for variables, always be explicit with function parameters and return types. This makes your code self-documenting and easier for others (and future you!) to understand. A 10-second type annotation saves 10 minutes of confusion later.
๐ฏ What's Next
Now that you understand primitives and type annotations, we'll explore how to work with collections: objects, arrays, and tuples. You'll learn how to describe the shape of complex data structures.