TypeScript Programming Labs

Master TypeScript's advanced type system features including union types, type guards, and generics.

Advanced Types - Module 2

Learn union types, literal types, type guards, and generics for powerful type-safe code.

Lab 4: Union Types & Literal Types
Intermediate
Coding Challenge
Your Task: Learn to use union types and literal types to create flexible yet type-safe code. Union types allow a variable to hold one of several types.

Detailed Requirements:
1. Create a union type variable: Declare a variable id that can be either a string OR a number. Use the pipe symbol | to create unions:
let id: string | number = 123; id = "ABC123"; // Both are valid!

2. Create a type alias for a union: Use the type keyword to create a reusable type alias called StringOrNumber:
type StringOrNumber = string | number;

3. Create literal types: Literal types restrict a variable to specific values. Create a type Direction that can ONLY be "north", "south", "east", or "west":
type Direction = "north" | "south" | "east" | "west"; let heading: Direction = "north"; // OK // heading = "up"; // ERROR: "up" is not assignable

4. Create a Status type: Create a literal type Status with values "pending", "approved", "rejected".

5. Create a function with union parameter: Write a function formatId that accepts id: string | number and returns a formatted string. Handle both types appropriately.

6. Use the types: Create variables using your types and call your function.

Why Union Types?
• Model real-world data that can be multiple types (API responses, user input)
• Avoid using any while maintaining flexibility
• TypeScript narrows the type based on your checks

Expected Output:
ID (number): 123 ID (string): ABC-456 Formatted: ID-123 Formatted: ID-ABC-456 Direction: north Status: approved

Requirements Checklist

Create variable with union type (string | number)
Create type alias StringOrNumber
Create Direction literal type with 4 directions
Create Status literal type
Create formatId function accepting union type
Use all types with console.log outputs
Output
// Click "Run Code" to compile and execute // TypeScript compiler output will appear here
Hints & Tips
• Union type: let id: string | number = 123;
• Type alias: type StringOrNumber = string | number;
• Literal type: type Direction = "north" | "south" | "east" | "west";
• Function with union: function formatId(id: string | number): string { return "ID-" + id; }
• Reassign union: id = 123; id = "ABC"; both work!
Progress: 0/6
Score: 0/100
0%

Lab Results

Review feedback below

Lab 5: Type Guards & Narrowing
Intermediate
Coding Challenge
Your Task: Learn type guards to safely narrow union types. When you have a union type, TypeScript needs you to check which type you're dealing with before accessing type-specific properties.

Detailed Requirements:
1. Use typeof type guard: Create a function processValue that takes value: string | number. Use typeof to check the type and handle each differently:
function processValue(value: string | number): string { if (typeof value === "string") { return value.toUpperCase(); // TS knows it's string here! } else { return (value * 2).toString(); // TS knows it's number here! } }

2. Create interfaces for type guard: Create two interfaces Bird (with fly(): void) and Fish (with swim(): void). Both should have a name: string property.

3. Use 'in' operator type guard: Create a function move that takes animal: Bird | Fish. Use the in operator to check which methods exist:
function move(animal: Bird | Fish): void { if ("fly" in animal) { animal.fly(); // TypeScript knows it's Bird } else { animal.swim(); // TypeScript knows it's Fish } }

4. Create a custom type guard: Write a function isBird that returns animal is Bird. This is a type predicate:
function isBird(animal: Bird | Fish): animal is Bird { return "fly" in animal; }

5. Use instanceof type guard: Create classes Car and Bicycle with different methods. Create a function that uses instanceof to narrow the type.

Why Type Guards?
• Safely access properties/methods that only exist on certain types
• TypeScript automatically narrows the type inside the guard
• Prevents runtime errors from accessing undefined properties

Expected Output:
Process "hello": HELLO Process 42: 84 Sparrow is flying! Nemo is swimming! Is sparrow a bird? true Car is driving at 60 mph Bicycle is pedaling

Requirements Checklist

Create processValue function with typeof guard
Create Bird and Fish interfaces
Create move function with 'in' operator guard
Create custom type guard function isBird
Create Car and Bicycle classes with instanceof guard
Test all type guards with console.log
Output
// Click "Run Code" to compile and execute
Hints & Tips
• typeof guard: if (typeof value === "string") { ... }
• 'in' operator: if ("fly" in animal) { animal.fly(); }
• Type predicate: function isBird(a: Bird | Fish): a is Bird
• instanceof: if (vehicle instanceof Car) { vehicle.drive(); }
• Interface method: interface Bird { fly(): void; }
Progress: 0/6
Score: 0/100
0%

Lab Results

Review feedback below

Lab 6: Generics
Advanced
Coding Challenge
Your Task: Master generics to create reusable, type-safe components. Generics allow you to write code that works with multiple types while preserving type information.

Detailed Requirements:
1. Create a generic identity function: The simplest generic - a function that returns what it receives, preserving the type:
function identity<T>(arg: T): T { return arg; } // Usage: let num = identity<number>(42); // num is number let str = identity<string>("hi"); // str is string

2. Create a generic function with arrays: Create getFirst<T> that takes an array of T and returns the first element (or undefined):
function getFirst<T>(arr: T[]): T | undefined { return arr[0]; }

3. Create a generic interface: Create a Box<T> interface with a contents: T property and getContents(): T method.

4. Create a generic class: Create a Stack<T> class with:
   • Private array items: T[]
   • push(item: T): void - add to stack
   • pop(): T | undefined - remove and return top
   • peek(): T | undefined - view top without removing

5. Use generic constraints: Create a function that requires the generic type to have a length property:
interface Lengthwise { length: number; } function logLength<T extends Lengthwise>(arg: T): void { console.log(arg.length); }

6. Multiple type parameters: Create a function makePair<K, V> that creates an object with key and value.

Why Generics?
• Write once, use with any type
• Maintain type safety (unlike any)
• Foundation for libraries like React, RxJS

Expected Output:
Identity number: 42 Identity string: Hello First of [1,2,3]: 1 First of ["a","b"]: a Box contains: TypeScript Book Stack: pushed 1, 2, 3 Stack pop: 3 Stack peek: 2 Length of "hello": 5 Length of [1,2,3]: 3 Pair: { key: "name", value: "Alice" }

Requirements Checklist

Create generic identity function
Create getFirst generic function for arrays
Create Box<T> generic interface
Create Stack<T> generic class with push/pop/peek
Create constrained generic with extends
Test all generics with console.log
Output
// Click "Run Code" to compile and execute
Hints & Tips
• Generic function: function identity<T>(arg: T): T { return arg; }
• Generic interface: interface Box<T> { contents: T; }
• Generic class: class Stack<T> { private items: T[] = []; }
• Constraint: function fn<T extends Lengthwise>(arg: T)
• Multiple params: function makePair<K, V>(key: K, value: V)
Progress: 0/6
Score: 0/100
0%

Lab Results

Review feedback below