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()) # worldType 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.0JavaScript 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()); // worldType 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.0Comparison
| 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
- Add type annotations to a Python function.
- Create a generic
Queueclass in TypeScript. - Define a
Userinterface in TypeScript and aUserProtocol in Python.
Next Steps
Now you know about type systems. Next, we'll learn about variables and data types.