TypeScript Programming Labs

Master expert-level TypeScript: mapped types, conditional types, and declaration files for library-grade code.

Expert Level - Module 5

Deep dive into mapped types, conditional types, and type declaration files.

Lab 13: Mapped Types
Expert
Coding Challenge
Your Task: Master mapped types - a powerful way to create new types by transforming properties of existing types. This is how TypeScript's built-in utility types like Partial, Required, and Readonly work internally.

Detailed Requirements:
1. Basic mapped type syntax: Transform each property of a type:
// Basic syntax: [K in keyof T] type MyReadonly<T> = { readonly [K in keyof T]: T[K]; }; interface User { name: string; age: number; } type ReadonlyUser = MyReadonly<User>; // Result: { readonly name: string; readonly age: number; }

2. Create Partial (make all optional):
type MyPartial<T> = { [K in keyof T]?: T[K]; }; type PartialUser = MyPartial<User>; // Result: { name?: string; age?: number; }

3. Create Required (make all required):
type MyRequired<T> = { [K in keyof T]-?: T[K]; // -? removes optional }; interface Config { debug?: boolean; verbose?: boolean; } type RequiredConfig = MyRequired<Config>; // Result: { debug: boolean; verbose: boolean; }

4. Create Pick (select specific keys):
type MyPick<T, K extends keyof T> = { [P in K]: T[P]; }; interface Person { name: string; age: number; email: string; } type NameAndAge = MyPick<Person, "name" | "age">; // Result: { name: string; age: number; }

5. Create Record (construct object type):
type MyRecord<K extends keyof any, V> = { [P in K]: V; }; type StringMap = MyRecord<"a" | "b" | "c", number>; // Result: { a: number; b: number; c: number; }

6. Key remapping (as clause):
type Getters<T> = { [K in keyof T as \`get\${Capitalize<string & K>}\`]: () => T[K]; }; type UserGetters = Getters<User>; // Result: { getName: () => string; getAge: () => number; }

Expected Output:
MyReadonly works: properties are readonly MyPartial works: properties are optional MyPick works: selected properties only MyRecord works: key-value mapping created Getters created: getName, getAge

Requirements Checklist

Create MyReadonly<T> mapped type
Create MyPartial<T> with optional modifier
Create MyRequired<T> with -? modifier
Create MyPick<T, K> for property selection
Create MyRecord<K, V> for object construction
Test mapped types with example interfaces
Output
// Click "Run Code" to compile and execute
Hints & Tips
• Basic syntax: type T<X> = { [K in keyof X]: X[K] }
• Add readonly: readonly [K in keyof T]
• Add optional: [K in keyof T]?
• Remove optional: [K in keyof T]-?
• Constrain keys: K extends keyof T
Progress: 0/6
Score: 0/100
0%

Lab Results

Review feedback below

Lab 14: Conditional Types
Expert
Coding Challenge
Your Task: Master conditional types - TypeScript's way of expressing type-level if/else logic. This enables powerful type transformations based on conditions.

Detailed Requirements:
1. Basic conditional type syntax:
// T extends U ? X : Y type IsString<T> = T extends string ? true : false; type A = IsString<string>; // true type B = IsString<number>; // false

2. Extract and Exclude types:
// Extract: keep types that match type MyExtract<T, U> = T extends U ? T : never; type Numbers = MyExtract<string | number | boolean, number>; // Result: number // Exclude: remove types that match type MyExclude<T, U> = T extends U ? never : T; type NotNumbers = MyExclude<string | number | boolean, number>; // Result: string | boolean

3. NonNullable type:
type MyNonNullable<T> = T extends null | undefined ? never : T; type MaybeString = string | null | undefined; type DefinitelyString = MyNonNullable<MaybeString>; // Result: string

4. Infer keyword - extract types from structures:
// Extract return type of a function type MyReturnType<T> = T extends (...args: any[]) => infer R ? R : never; type Fn = (x: number) => string; type FnReturn = MyReturnType<Fn>; // string // Extract element type from array type ArrayElement<T> = T extends (infer E)[] ? E : never; type NumArray = number[]; type Element = ArrayElement<NumArray>; // number

5. Extract function parameters:
type MyParameters<T> = T extends (...args: infer P) => any ? P : never; type Fn2 = (a: string, b: number) => void; type Params = MyParameters<Fn2>; // [string, number]

6. Distributive conditional types:
// Unions are distributed automatically type ToArray<T> = T extends any ? T[] : never; type Result = ToArray<string | number>; // Result: string[] | number[] (not (string | number)[])

Expected Output:
IsString<string> = true MyExtract<string|number, number> = number MyReturnType works: extracted return type MyParameters works: extracted params Conditional types demonstration complete!

Requirements Checklist

Create IsString<T> conditional type
Create MyExtract<T, U> type
Create MyExclude<T, U> type
Create MyReturnType<T> using infer
Create MyParameters<T> using infer
Test conditional types with examples
Output
// Click "Run Code" to compile and execute
Hints & Tips
• Conditional syntax: T extends U ? X : Y
• Use never to filter out types
infer R captures a type in the pattern
• Function pattern: (...args: any[]) => infer R
• Params pattern: (...args: infer P) => any
Progress: 0/6
Score: 0/100
0%

Lab Results

Review feedback below

Lab 15: Declaration Files (.d.ts)
Expert
Coding Challenge
Your Task: Learn to write TypeScript declaration files (.d.ts) to add types to JavaScript libraries. This is essential when using third-party JS libraries or publishing your own.

Detailed Requirements:
1. Declare a module: Add types to an existing JS library:
// my-library.d.ts declare module "my-library" { export function greet(name: string): string; export function calculate(a: number, b: number): number; export const VERSION: string; }

2. Declare global variables: Types for globals like window extensions:
// globals.d.ts declare global { interface Window { myApp: { version: string; init(): void; }; } var DEBUG: boolean; function legacyFunction(x: string): void; }

3. Declare a class:
declare class MyWidget { constructor(element: HTMLElement); render(): void; destroy(): void; readonly id: string; }

4. Declare interfaces and types:
declare interface Config { apiUrl: string; timeout?: number; retries?: number; } declare type EventHandler = (event: Event) => void; declare interface EventEmitter { on(event: string, handler: EventHandler): void; off(event: string, handler: EventHandler): void; emit(event: string, data?: any): void; }

5. Declare namespaces: Group related declarations:
declare namespace MyLib { interface Options { debug: boolean; logLevel: "info" | "warn" | "error"; } function init(options: Options): void; function shutdown(): void; namespace Utils { function format(str: string): string; function parse(data: string): object; } }

6. Ambient module declarations: For modules without types:
// For JSON imports declare module "*.json" { const value: any; export default value; } // For CSS modules declare module "*.css" { const classes: { [key: string]: string }; export default classes; } // For image imports declare module "*.png" { const src: string; export default src; }

Expected Output:
Module declaration created Global types declared Class declaration valid Namespace declaration complete Ambient modules configured

Requirements Checklist

Declare a module with exports
Declare global variables/functions
Declare a class with methods
Declare interfaces and types
Declare a namespace with members
Create ambient module declarations
Output
// Click "Run Code" to compile and execute
Hints & Tips
• Module: declare module "name" { export ... }
• Global: declare global { interface Window { } }
• Class: declare class Name { method(): void; }
• Namespace: declare namespace Name { function fn(): void; }
• Ambient: declare module "*.ext" { }
Progress: 0/6
Score: 0/100
0%

Lab Results

Review feedback below

Expert Level Complete!

You've mastered advanced TypeScript concepts including mapped types, conditional types, and declaration files. You're now ready to build library-grade TypeScript code!