Documentation Index Fetch the complete documentation index at: https://mintlify.com/typescript-exercises/typescript-exercises/llms.txt
Use this file to discover all available pages before exploring further.
When using JavaScript libraries that lack TypeScript declarations, you need to write .d.ts files to describe their types. These exercises teach you how to provide type safety for external modules.
Exercise 11: Basic Module Declarations
Scenario
You’re using str-utils, a JavaScript library for string manipulation, but it has no TypeScript declarations. You need to create type definitions so TypeScript understands the library’s API.
The Problem
import {
strReverse ,
strToLower ,
strToUpper ,
strRandomize ,
strInvertCase
} from 'str-utils' ; // ❌ Error: Cannot find module 'str-utils'
export const nameDecorators = [
strReverse , // any
strToLower , // any
strToUpper , // any
strRandomize , // any
strInvertCase // any
];
Without type declarations, TypeScript treats all imports as any, losing type safety.
Understanding Declaration Files
Declaration files (.d.ts) describe the shape of JavaScript code without implementation:
// declarations/str-utils/index.d.ts
declare module 'str-utils' {
// Type declarations only, no implementation
export const strReverse : ( input : string ) => string ;
export const strToUpper : ( input : string ) => string ;
// ...
}
Key Concepts
Ambient Modules declare module tells TypeScript about external modules
Type Aliases Avoid duplication by creating reusable type definitions
Solution
Create a declaration file at declarations/str-utils/index.d.ts:
// declarations/str-utils/index.d.ts
declare module 'str-utils' {
// export const ...
// export function ...
}
// declarations/str-utils/index.d.ts
declare module 'str-utils' {
type StrUtil = ( input : string ) => string ;
export const strReverse : StrUtil ;
export const strToLower : StrUtil ;
export const strToUpper : StrUtil ;
export const strRandomize : StrUtil ;
export const strInvertCase : StrUtil ;
}
All the functions have the same signature: (input: string) => string Without alias: export const strReverse : ( input : string ) => string ;
export const strToLower : ( input : string ) => string ;
export const strToUpper : ( input : string ) => string ;
// Repetitive and error-prone
With alias: type StrUtil = ( input : string ) => string ;
export const strReverse : StrUtil ;
export const strToLower : StrUtil ;
export const strToUpper : StrUtil ;
// DRY and maintainable
Using the Typed Library
Now TypeScript understands the library:
import { strReverse , strToUpper } from 'str-utils' ;
function logPerson ( person : Person ) {
const randomDecorator = nameDecorators [
Math . round ( Math . random () * ( nameDecorators . length - 1 ))
];
const name = randomDecorator ( person . name ); // Type-safe!
console . log ( ` ${ name } , ${ person . age } ` );
}
// Type checking works
strReverse ( 'hello' ); // ✅ OK
strReverse ( 123 ); // ❌ Error: Argument of type 'number' is not assignable
const result = strToUpper ( 'test' ); // result: string
Exercise 12: Advanced Generic Declarations
Scenario
You’re using stats, a library with functions for finding max, min, median, and average values. These functions are generic and work with any type, using comparator functions.
The Problem
import {
getMaxIndex ,
getMaxElement ,
getMinIndex ,
getMinElement ,
getMedianIndex ,
getMedianElement ,
getAverageValue
} from 'stats' ; // ❌ Error: Cannot find module 'stats'
const compareUsers = ( a : User , b : User ) => a . age - b . age ;
const youngestUser = getMinElement ( users , compareUsers ); // any
Understanding the Library
The library has three categories of functions:
Index Functions
Return the index of an element getMaxIndex ( users , compareUsers ) // Returns: number
getMinIndex ( users , compareUsers ) // Returns: number
getMedianIndex ( users , compareUsers ) // Returns: number
Element Functions
Return the element itself or null getMaxElement ( users , compareUsers ) // Returns: User | null
getMinElement ( users , compareUsers ) // Returns: User | null
getMedianElement ( users , compareUsers ) // Returns: User | null
Value Function
Return a computed numeric value getAverageValue ( users , ( u ) => u . age ) // Returns: number | null
Solution
Create a declaration file with generic types:
// declarations/stats/index.d.ts
declare module 'stats' {
export function getMaxIndex ( input : unknown , comparator : unknown ) : unknown ;
}
// declarations/stats/index.d.ts
declare module 'stats' {
type Comparator < T > = ( a : T , b : T ) => number ;
type GetIndex = < T >( input : T [], comparator : Comparator < T >) => number ;
export const getMaxIndex : GetIndex ;
export const getMinIndex : GetIndex ;
export const getMedianIndex : GetIndex ;
type GetElement = < T >( input : T [], comparator : Comparator < T >) => T | null ;
export const getMaxElement : GetElement ;
export const getMinElement : GetElement ;
export const getMedianElement : GetElement ;
export const getAverageValue : < T >(
input : T [],
getValue : ( item : T ) => number
) => number | null ;
}
Breaking Down the Solution
type Comparator < T > = ( a : T , b : T ) => number ;
A comparator takes two items of the same type and returns:
Negative number if a < b
Zero if a === b
Positive number if a > b
Example: const compareUsers : Comparator < User > = ( a , b ) => a . age - b . age ;
const compareAdmins : Comparator < Admin > = ( a , b ) => a . age - b . age ;
type GetIndex = < T >( input : T [], comparator : Comparator < T >) => number ;
This is a generic function type that:
Takes an array of any type T[]
Takes a comparator for that type
Returns the index as a number
All three index functions share this signature, so we define it once.
type GetElement = < T >( input : T [], comparator : Comparator < T >) => T | null ;
Similar to GetIndex, but returns the element itself (or null if the array is empty).
Using the Typed Library
const users : User [] = [
{ type: 'user' , name: 'Max Mustermann' , age: 25 , occupation: 'Chimney sweep' },
{ type: 'user' , name: 'Kate Müller' , age: 23 , occupation: 'Astronaut' },
{ type: 'user' , name: 'Moses' , age: 70 , occupation: 'Desert guide' }
];
const compareUsers = ( a : User , b : User ) => a . age - b . age ;
// All properly typed now
const youngestUser = getMinElement ( users , compareUsers ); // User | null
const oldestIndex = getMaxIndex ( users , compareUsers ); // number
const medianUser = getMedianElement ( users , compareUsers ); // User | null
// Type checking works
if ( youngestUser ) {
console . log ( youngestUser . name , youngestUser . age ); // ✅ OK
}
// Errors caught at compile time
getMinElement ( users , ( a , b ) => a . name ); // ❌ Error: Type 'string' not assignable to 'number'
export const getAverageValue : < T >(
input : T [],
getValue : ( item : T ) => number
) => number | null ;
// Usage
const avgUserAge = getAverageValue ( users , ( u ) => u . age );
// Type: number | null
const avgAdminAge = getAverageValue ( admins , ( a ) => a . age );
// Type: number | null
console . log ( `Average user age: ${ avgUserAge } years` );
Declaration File Best Practices
Avoid Duplication Use type aliases for repeated signatures
Match Runtime Behavior Declarations should accurately reflect the JavaScript API
Be Specific Use precise types rather than any or unknown
Document Edge Cases Use | null or | undefined when functions can return these
Common Declaration Patterns
Constants
Functions
Classes
Mixed Exports
declare module 'my-lib' {
export const VERSION : string ;
export const CONFIG : { timeout : number ; retries : number ; };
}
What You Learned
Use declare module 'name' to define types for external JavaScript modules
Write generic type definitions that work with any type while maintaining safety
Type Aliases in Declarations
Reduce duplication by defining reusable type aliases
Use T | null to represent functions that might not return a value
For popular libraries, check DefinitelyTyped before writing your own declarations. Many community-maintained types are available via @types/* packages.
Next Steps
Move on to Module Augmentation to learn how to extend existing type declarations.
Additional Resources