Skip to content

Type System

Type Annotations

Python Type Annotations

Basic Type Annotations

python
# Python - Basic type annotations
from typing import List, Dict, Optional, Union, Any

# Variable type annotations
name: str = "Alice"
age: int = 25
height: float = 1.75
is_student: bool = True

# Function parameter and return value type annotations
def greet(name: str) -> str:
    return f"Hello, {name}!"

def add_numbers(a: int, b: int) -> int:
    return a + b

def calculate_average(numbers: List[float]) -> float:
    if not numbers:
        return 0.0
    return sum(numbers) / len(numbers)

# Complex type annotations
def process_user_data(
    user_id: int,
    name: str,
    age: Optional[int] = None,
    hobbies: List[str] = None
) -> Dict[str, Any]:
    result = {
        "id": user_id,
        "name": name,
        "age": age,
        "hobbies": hobbies or []
    }
    return result

# Using type annotations
result = greet("Bob")  # Type checker will validate the parameter type
print(result)  # Hello, Bob!

numbers = [1.5, 2.5, 3.5]
avg = calculate_average(numbers)
print(f"Average: {avg}")  # Average: 2.5

user_data = process_user_data(1, "Charlie", 30, ["reading", "coding"])
print(user_data)

Advanced Type Annotations

python
# Python - Advanced type annotations
from typing import (
    List, Dict, Optional, Union, Any, Tuple,
    Callable, TypeVar, Generic, Protocol
)

# Union type
def process_data(data: Union[str, int, float]) -> str:
    return str(data)

# Using | syntax (Python 3.10+)
def process_data_v2(data: str | int | float) -> str:
    return str(data)

# Optional type
def get_user_age(user_id: int) -> Optional[int]:
    # Simulate database query
    users = {1: 25, 2: 30}
    return users.get(user_id)

# Callable type
def apply_operation(
    func: Callable[[int, int], int],
    a: int,
    b: int
) -> int:
    return func(a, b)

def add(a: int, b: int) -> int:
    return a + b

def multiply(a: int, b: int) -> int:
    return a * b

# Using Callable
result1 = apply_operation(add, 5, 3)      # 8
result2 = apply_operation(multiply, 5, 3) # 15

# Generic type
T = TypeVar('T')

class Stack(Generic[T]):
    def __init__(self) -> None:
        self.items: List[T] = []

    def push(self, item: T) -> None:
        self.items.append(item)

    def pop(self) -> T:
        if not self.items:
            raise IndexError("Stack is empty")
        return self.items.pop()

    def peek(self) -> Optional[T]:
        return self.items[-1] if self.items else None

# Using generics
int_stack = Stack[int]()
int_stack.push(1)
int_stack.push(2)
print(int_stack.pop())  # 2

str_stack = Stack[str]()
str_stack.push("hello")
str_stack.push("world")
print(str_stack.pop())  # world

Type Aliases and Protocols

python
# Python - Type aliases and protocols
from typing import TypeAlias, Protocol

# Type alias
UserId = int
UserName = str
UserAge = int
UserData = Dict[str, Union[str, int, List[str]]]

# Using type aliases
def create_user(user_id: UserId, name: UserName, age: UserAge) -> UserData:
    return {
        "id": user_id,
        "name": name,
        "age": age,
        "hobbies": []
    }

# Protocol (structural typing)
class Drawable(Protocol):
    def draw(self) -> str:
        ...

class Circle:
    def __init__(self, radius: float):
        self.radius = radius

    def draw(self) -> str:
        return f"Drawing circle with radius {self.radius}"

class Rectangle:
    def __init__(self, width: float, height: float):
        self.width = width
        self.height = height

    def draw(self) -> str:
        return f"Drawing rectangle {self.width}x{self.height}"

def draw_shape(shape: Drawable) -> str:
    return shape.draw()

# Using Protocol
circle = Circle(5.0)
rectangle = Rectangle(10.0, 5.0)

print(draw_shape(circle))     # Drawing circle with radius 5.0
print(draw_shape(rectangle))  # Drawing rectangle 10.0x5.0

JavaScript Type Annotations (TypeScript)

Basic Type Annotations

typescript
// TypeScript - Basic type annotations
// Variable type annotations
let name: string = "Alice";
let age: number = 25;
let height: number = 1.75;
let isStudent: boolean = true;

// Function parameter and return value type annotations
function greet(name: string): string {
  return `Hello, ${name}!`;
}

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

function calculateAverage(numbers: number[]): number {
  if (numbers.length === 0) {
    return 0;
  }
  return numbers.reduce((sum, num) => sum + num, 0) / numbers.length;
}

// Complex type annotations
interface UserData {
  id: number;
  name: string;
  age?: number; // Optional property
  hobbies: string[];
}

function processUserData(
  userId: number,
  name: string,
  age?: number,
  hobbies: string[] = []
): UserData {
  return {
    id: userId,
    name,
    age,
    hobbies,
  };
}

// Using type annotations
const result = greet("Bob");
console.log(result); // Hello, Bob!

const numbers = [1.5, 2.5, 3.5];
const avg = calculateAverage(numbers);
console.log(`Average: ${avg}`); // Average: 2.5

const userData = processUserData(1, "Charlie", 30, ["reading", "coding"]);
console.log(userData);

Advanced Type Annotations

typescript
// TypeScript - Advanced type annotations

// Union type
function processData(data: string | number | boolean): string {
  return String(data);
}

// Optional type (union with undefined)
function getUserAge(userId: number): number | undefined {
  const users = new Map<number, number>([
    [1, 25],
    [2, 30],
  ]);
  return users.get(userId);
}

// Callable type
type Operation = (a: number, b: number) => number;

function applyOperation(func: Operation, a: number, b: number): number {
  return func(a, b);
}

const add: Operation = (a, b) => a + b;
const multiply: Operation = (a, b) => a * b;

const result1 = applyOperation(add, 5, 3); // 8
const result2 = applyOperation(multiply, 5, 3); // 15

// Generic type
class Stack<T> {
  private items: T[] = [];

  push(item: T): void {
    this.items.push(item);
  }

  pop(): T | undefined {
    return this.items.pop();
  }

  peek(): T | undefined {
    return this.items[this.items.length - 1];
  }
}

// Using generics
const intStack = new Stack<number>();
intStack.push(1);
intStack.push(2);
console.log(intStack.pop()); // 2

const strStack = new Stack<string>();
strStack.push("hello");
strStack.push("world");
console.log(strStack.pop()); // world

Type Aliases and Interfaces

typescript
// TypeScript - Type aliases and interfaces

// Type alias
type UserId = number;
type UserName = string;
type UserAge = number;

// Interface
interface UserData {
  id: UserId;
  name: UserName;
  age?: UserAge;
  hobbies: string[];
}

function createUser(userId: UserId, name: UserName, age?: UserAge): UserData {
  return {
    id: userId,
    name,
    age,
    hobbies: [],
  };
}

// Structural typing with interfaces
interface Drawable {
  draw(): string;
}

class Circle {
  constructor(private radius: number) {}

  draw(): string {
    return `Drawing circle with radius ${this.radius}`;
  }
}

class Rectangle {
  constructor(private width: number, private height: number) {}

  draw(): string {
    return `Drawing rectangle ${this.width}x${this.height}`;
  }
}

function drawShape(shape: Drawable): string {
  return shape.draw();
}

const circle = new Circle(5.0);
const rectangle = new Rectangle(10.0, 5.0);

console.log(drawShape(circle)); // Drawing circle with radius 5.0
console.log(drawShape(rectangle)); // Drawing rectangle 10.0x5.0

Comparison

| Feature | Python (typing) | TypeScript | | -------------- | ----------------------------- | -------------------------------- | ---------------- | --- | | Basic Types | str, int, float, bool | string, number, boolean | | Complex Types | List, Dict, Tuple | Array, object, [type, ...] | | Union Types | Union[A, B] or A | B | A | B | | Optional Types | Optional[T] | T | undefinedorT? | | Generics | TypeVar, Generic | <T> | | Interfaces | Protocol (structural) | interface | | Type Checking | Runtime (e.g., mypy) | Compile-time |

Summary

  • Python: Gradual typing, flexible, relies on runtime checkers.
  • TypeScript: Static typing, powerful tooling, part of the compile process.

Exercises

  1. Add type annotations to a Python function.
  2. Create a generic Queue class in TypeScript.
  3. Define a User interface in TypeScript and a User Protocol in Python.

Next Steps

Now you know about type systems. Next, we'll learn about variables and data types.