Falytom logoFALYTOM
Games
ToolsUI ComponentsServicesProductsAbout
LoginSign Up
menu
  1. Home
  2. /Web Development
  3. /Typescript
  4. /Type Declarations
Your Progress0%
0 of 30 completed

TypeScript Topics

Getting Started

  • What is TypeScript?
  • TypeScript vs JavaScript
  • Setting Up TypeScript
  • Your First TypeScript Program

Basic Types

  • Primitive Types
  • Arrays and Tuples
  • Objects and Type Aliases
  • Enums
  • Any, Unknown, and Never

Functions & Interfaces

  • Function Types and Signatures
  • Interfaces
  • Optional and Default Parameters
  • Function Overloading

Advanced Types

  • Union and Intersection Types
  • Type Guards and Narrowing
  • Literal Types
  • Type Assertions and Casting

Generics

  • Introduction to Generics
  • Generic Constraints
  • Utility Types

Classes & OOP

  • Classes and Constructors
  • Access Modifiers
  • Inheritance and Polymorphism
  • Abstract Classes and Interfaces

Practical TypeScript

  • Working with Modules
  • TypeScript with React
  • Async/Await and Promises
  • Working with APIs and JSON
  • Type Declarations and DefinitelyTyped
  • TypeScript Best Practices

Type Declarations and DefinitelyTyped

Using @types packages and creating custom type declarations

Type declarations (.d.ts files) allow TypeScript to understand JavaScript libraries and provide type checking and autocomplete. DefinitelyTyped is a massive repository of type declarations for thousands of JavaScript libraries. Let's master type declarations!

DefinitelyTyped and @types Packages

DefinitelyTyped hosts community-maintained type declarations for JavaScript libraries. Install them via npm with @types/ prefix.

terminal
# Install type declarations for popular libraries
npm install --save-dev @types/node
npm install --save-dev @types/react
npm install --save-dev @types/express
npm install --save-dev @types/lodash
npm install --save-dev @types/jest

# Or with a library that doesn't have built-in types
npm install lodash
npm install --save-dev @types/lodash

# TypeScript will automatically find these types
# No import needed for most cases
using-types.ts
// After installing @types/lodash
import _ from 'lodash';

// TypeScript now knows all lodash methods!
const numbers = [1, 2, 3, 4, 5];

const doubled = _.map(numbers, (n) => n * 2);
// TypeScript knows: doubled is number[]

const sum = _.sum(numbers);
// TypeScript knows: sum is number

const chunked = _.chunk(numbers, 2);
// TypeScript knows: chunked is number[][]

// After installing @types/node
import * as fs from 'fs';
import * as path from 'path';

// TypeScript knows Node.js APIs
fs.readFile('file.txt', 'utf8', (err, data) => {
  // err: NodeJS.ErrnoException | null
  // data: string
  if (err) {
    console.error(err);
    return;
  }
  console.log(data);
});

const filePath = path.join(__dirname, 'file.txt');
// filePath: string

// After installing @types/express
import express, { Request, Response, NextFunction } from 'express';

const app = express();

app.get('/users/:id', (req: Request, res: Response) => {
  const userId = req.params.id;  // string
  res.json({ id: userId });
});

app.use((err: Error, req: Request, res: Response, next: NextFunction) => {
  console.error(err.stack);
  res.status(500).send('Something broke!');
});

πŸ’‘ Finding Type Declarations

Search for @types/package-name on npm. If a package has built-in TypeScript support, you don't need @types. Check the package's package.json for a "types" or "typings" field.

Basic Declaration Files

global.d.ts
// Global type declarations
// These are available everywhere without imports

// Declare global variables
declare const API_URL: string;
declare const VERSION: string;
declare const DEBUG: boolean;

// Declare global functions
declare function log(message: string): void;
declare function formatPrice(amount: number): string;

// Declare global classes
declare class Storage {
  get(key: string): any;
  set(key: string, value: any): void;
  remove(key: string): void;
  clear(): void;
}

// Declare global interfaces
interface Window {
  gtag: (command: string, ...args: any[]) => void;
  dataLayer: any[];
}

// Declare global types
type UUID = string;
type Timestamp = number;
type Currency = 'NGN' | 'USD' | 'EUR';

// Now you can use these anywhere in your project
const url = API_URL;  // TypeScript knows this is string
const version = VERSION;  // string
const isDebug = DEBUG;  // boolean

log("Application started");  // OK
const price = formatPrice(50000);  // Returns string

const storage = new Storage();
storage.set('user', { id: 1 });

// Using augmented Window interface
window.gtag('config', 'GA_MEASUREMENT_ID');
window.dataLayer.push({ event: 'page_view' });

const userId: UUID = "550e8400-e29b-41d4-a716-446655440000";
const now: Timestamp = Date.now();
const currency: Currency = 'NGN';

Module Declarations

custom-module.d.ts
// Declare a module that doesn't have types
declare module 'my-js-library' {
  // Export types for the module
  export interface Config {
    apiKey: string;
    timeout: number;
  }
  
  export class Client {
    constructor(config: Config);
    request(endpoint: string): Promise<any>;
  }
  
  export function initialize(apiKey: string): void;
  export function getVersion(): string;
  
  // Default export
  export default Client;
}

// Declare module for JSON files
declare module '*.json' {
  const value: any;
  export default value;
}

// Declare module for CSS files
declare module '*.css' {
  const classes: { [key: string]: string };
  export default classes;
}

// Declare module for image files
declare module '*.png' {
  const src: string;
  export default src;
}

declare module '*.jpg' {
  const src: string;
  export default src;
}

declare module '*.svg' {
  const src: string;
  export default src;
}

// Declare module for environment variables
declare module 'process' {
  global {
    namespace NodeJS {
      interface ProcessEnv {
        NODE_ENV: 'development' | 'production' | 'test';
        API_URL: string;
        DATABASE_URL: string;
        SECRET_KEY: string;
      }
    }
  }
}

// Using the declarations
import Client, { Config, initialize } from 'my-js-library';

const config: Config = {
  apiKey: 'abc123',
  timeout: 5000
};

const client = new Client(config);
initialize('api-key');

// Using JSON import
import data from './data.json';
console.log(data);

// Using CSS import
import styles from './App.css';
const className = styles.container;

// Using image import
import logo from './logo.png';
console.log(logo);  // string (image path)

// Using process.env
const apiUrl = process.env.API_URL;  // string
const nodeEnv = process.env.NODE_ENV;  // 'development' | 'production' | 'test'

Namespace Declarations

jquery.d.ts
// Declare namespace for libraries like jQuery
declare namespace $ {
  // Static methods
  function ajax(url: string, settings?: AjaxSettings): JqXHR;
  function get(url: string, data?: any, success?: Function): JqXHR;
  function post(url: string, data?: any, success?: Function): JqXHR;
  
  // Nested namespace
  namespace fn {
    function extend(obj: any): void;
  }
  
  // Interfaces
  interface AjaxSettings {
    method?: string;
    data?: any;
    success?: (data: any) => void;
    error?: (error: any) => void;
  }
  
  interface JqXHR {
    done(callback: Function): JqXHR;
    fail(callback: Function): JqXHR;
    always(callback: Function): JqXHR;
  }
}

// Using the jQuery declarations
$.ajax('/api/users', {
  method: 'GET',
  success: (data) => {
    console.log(data);
  },
  error: (error) => {
    console.error(error);
  }
});

$.get('/api/products').done((data) => {
  console.log(data);
});

// Declare namespace for your own library
declare namespace MyUtils {
  function formatCurrency(amount: number): string;
  function parseDate(date: string): Date;
  
  namespace String {
    function capitalize(str: string): string;
    function truncate(str: string, length: number): string;
  }
  
  namespace Array {
    function chunk<T>(arr: T[], size: number): T[][];
    function unique<T>(arr: T[]): T[];
  }
}

// Using custom namespace
const price = MyUtils.formatCurrency(50000);
const capitalized = MyUtils.String.capitalize("hello");
const chunks = MyUtils.Array.chunk([1, 2, 3, 4, 5], 2);

Module Augmentation

express-augmentation.d.ts
// Extend existing module types
// Add custom properties to Express Request

import 'express';

declare module 'express' {
  interface Request {
    user?: {
      id: number;
      email: string;
      role: string;
    };
    
    requestId?: string;
    startTime?: number;
  }
  
  interface Response {
    sendSuccess(data: any): Response;
    sendError(message: string, statusCode?: number): Response;
  }
}

// Now you can use the extended types
import { Request, Response, NextFunction } from 'express';

function authMiddleware(req: Request, res: Response, next: NextFunction) {
  // TypeScript now knows about req.user
  req.user = {
    id: 1,
    email: 'yusuf@example.com',
    role: 'admin'
  };
  
  req.requestId = 'req-' + Date.now();
  req.startTime = Date.now();
  
  next();
}

function getUserHandler(req: Request, res: Response) {
  if (!req.user) {
    return res.sendError('Unauthorized', 401);
  }
  
  res.sendSuccess({
    user: req.user,
    requestId: req.requestId
  });
}

// Augment array prototype
declare global {
  interface Array<T> {
    first(): T | undefined;
    last(): T | undefined;
    random(): T | undefined;
  }
}

// Now arrays have these methods (in types)
const numbers = [1, 2, 3, 4, 5];
const first = numbers.first();    // number | undefined
const last = numbers.last();      // number | undefined
const random = numbers.random();  // number | undefined

// Augment String prototype
declare global {
  interface String {
    capitalize(): string;
    truncate(length: number): string;
  }
}

const text = "hello world";
const capitalized = text.capitalize();  // string
const truncated = text.truncate(5);     // string

Practical Examples

Example 1: Creating Types for Legacy JavaScript Library

legacy-chart-lib.d.ts
// Typing a legacy charting library without TypeScript support

declare module 'legacy-charts' {
  // Chart configuration
  export interface ChartConfig {
    type: 'line' | 'bar' | 'pie' | 'doughnut';
    data: ChartData;
    options?: ChartOptions;
  }
  
  export interface ChartData {
    labels: string[];
    datasets: Dataset[];
  }
  
  export interface Dataset {
    label: string;
    data: number[];
    backgroundColor?: string | string[];
    borderColor?: string | string[];
    borderWidth?: number;
  }
  
  export interface ChartOptions {
    responsive?: boolean;
    maintainAspectRatio?: boolean;
    title?: {
      display: boolean;
      text: string;
    };
    legend?: {
      display: boolean;
      position?: 'top' | 'bottom' | 'left' | 'right';
    };
    scales?: {
      y?: {
        beginAtZero: boolean;
      };
    };
  }
  
  // Main Chart class
  export class Chart {
    constructor(canvas: HTMLCanvasElement, config: ChartConfig);
    
    update(): void;
    destroy(): void;
    reset(): void;
    
    data: ChartData;
    options: ChartOptions;
  }
  
  // Utility functions
  export function createChart(
    canvas: HTMLCanvasElement,
    config: ChartConfig
  ): Chart;
  
  export function getDefaults(type: ChartConfig['type']): ChartOptions;
}

// Using the typed library
import { Chart, ChartConfig, createChart } from 'legacy-charts';

const config: ChartConfig = {
  type: 'bar',
  data: {
    labels: ['Jan', 'Feb', 'Mar', 'Apr', 'May'],
    datasets: [{
      label: 'Sales (₦)',
      data: [50000, 75000, 60000, 90000, 85000],
      backgroundColor: 'rgba(75, 192, 192, 0.2)',
      borderColor: 'rgba(75, 192, 192, 1)',
      borderWidth: 1
    }]
  },
  options: {
    responsive: true,
    title: {
      display: true,
      text: 'Monthly Sales'
    },
    scales: {
      y: {
        beginAtZero: true
      }
    }
  }
};

const canvas = document.getElementById('myChart') as HTMLCanvasElement;
const chart = new Chart(canvas, config);

// Update data
chart.data.datasets[0].data = [55000, 80000, 65000, 95000, 90000];
chart.update();

Example 2: Global Script Variables

global-scripts.d.ts
// Type definitions for scripts loaded via <script> tags

// Google Analytics
declare global {
  interface Window {
    gtag: (
      command: 'config' | 'event' | 'set',
      targetId: string,
      params?: Record<string, any>
    ) => void;
    
    dataLayer: any[];
  }
}

// Facebook Pixel
declare global {
  interface Window {
    fbq: (
      command: 'track' | 'trackCustom' | 'init',
      event: string,
      params?: Record<string, any>
    ) => void;
  }
}

// Paystack
declare namespace Paystack {
  interface PaymentData {
    email: string;
    amount: number;
    currency?: string;
    ref?: string;
    metadata?: Record<string, any>;
  }
  
  interface PaymentResponse {
    reference: string;
    trans: string;
    status: 'success' | 'failed';
    message: string;
  }
  
  interface PaystackOptions {
    key: string;
    email: string;
    amount: number;
    currency?: string;
    ref?: string;
    metadata?: Record<string, any>;
    onClose?: () => void;
    callback?: (response: PaymentResponse) => void;
  }
  
  class PaystackPop {
    constructor();
    newTransaction(options: PaystackOptions): void;
  }
}

declare global {
  interface Window {
    PaystackPop: typeof Paystack.PaystackPop;
  }
}

// Using the global declarations
function trackPageView(page: string): void {
  if (window.gtag) {
    window.gtag('config', 'GA_MEASUREMENT_ID', {
      page_path: page
    });
  }
}

function trackEvent(eventName: string, params: Record<string, any>): void {
  if (window.gtag) {
    window.gtag('event', eventName, params);
  }
  
  if (window.fbq) {
    window.fbq('trackCustom', eventName, params);
  }
}

function initiatePayment(): void {
  const handler = new window.PaystackPop();
  
  handler.newTransaction({
    key: 'pk_test_xxxxx',
    email: 'yusuf@example.com',
    amount: 50000 * 100,  // Amount in kobo
    currency: 'NGN',
    metadata: {
      custom_fields: [
        {
          display_name: 'Customer Name',
          variable_name: 'customer_name',
          value: 'Yusuf Ibrahim'
        }
      ]
    },
    callback: (response) => {
      console.log('Payment successful:', response.reference);
      trackEvent('purchase', {
        value: 50000,
        currency: 'NGN',
        transaction_id: response.reference
      });
    },
    onClose: () => {
      console.log('Payment cancelled');
    }
  });
}

// Track page view
trackPageView('/products');

// Track custom event
trackEvent('add_to_cart', {
  item_id: 'PROD-123',
  item_name: 'Laptop',
  value: 450000
});

Example 3: Environment Variables

env.d.ts
// Type-safe environment variables

declare global {
  namespace NodeJS {
    interface ProcessEnv {
      // Required environment variables
      NODE_ENV: 'development' | 'production' | 'test';
      DATABASE_URL: string;
      API_URL: string;
      SECRET_KEY: string;
      
      // Optional environment variables
      PORT?: string;
      REDIS_URL?: string;
      SMTP_HOST?: string;
      SMTP_PORT?: string;
      
      // Third-party API keys
      PAYSTACK_PUBLIC_KEY: string;
      PAYSTACK_SECRET_KEY: string;
      GOOGLE_ANALYTICS_ID?: string;
      FACEBOOK_PIXEL_ID?: string;
      
      // Feature flags
      ENABLE_REGISTRATION?: 'true' | 'false';
      ENABLE_PAYMENTS?: 'true' | 'false';
      DEBUG?: 'true' | 'false';
    }
  }
}

export {};

// Using typed environment variables
const config = {
  nodeEnv: process.env.NODE_ENV,
  database: process.env.DATABASE_URL,
  api: process.env.API_URL,
  port: parseInt(process.env.PORT || '3000'),
  
  paystack: {
    publicKey: process.env.PAYSTACK_PUBLIC_KEY,
    secretKey: process.env.PAYSTACK_SECRET_KEY
  },
  
  features: {
    registration: process.env.ENABLE_REGISTRATION === 'true',
    payments: process.env.ENABLE_PAYMENTS === 'true',
    debug: process.env.DEBUG === 'true'
  }
};

// TypeScript validates all these
console.log(`Running in ${config.nodeEnv} mode`);
console.log(`Database: ${config.database}`);
console.log(`Port: ${config.port}`);

if (config.features.payments) {
  console.log('Payments enabled');
}

// Helper function with type checking
function getEnvVar(key: keyof NodeJS.ProcessEnv): string {
  const value = process.env[key];
  
  if (!value) {
    throw new Error(`Environment variable ${key} is not set`);
  }
  
  return value;
}

const apiUrl = getEnvVar('API_URL');  // Type-safe
// const invalid = getEnvVar('INVALID_KEY');  // ❌ TypeScript error!

Test Your Knowledge

Type Safety Check

Which declaration file pattern is correct?

// Option A - Global declaration
// myLibrary.d.ts
declare const VERSION: string;

declare function calculate(a: number, b: number): number;

declare class Calculator {
  add(a: number, b: number): number;
  subtract(a: number, b: number): number;
}

// Option B - Module declaration
// lodash.d.ts
declare module 'lodash' {
  export function map(arr: any[], fn: Function): any[];
  export function filter(arr: any[], fn: Function): any[];
}

// Option C - Namespace declaration
// jQuery.d.ts
declare namespace $ {
  function ajax(url: string): void;
  function get(url: string): void;
}

// Option D - Missing declare keyword
// utils.d.ts
const PI = 3.14159;  // ❌ Missing 'declare'

function add(a: number, b: number): number;  // ❌ Missing 'declare'

Common TypeScript Error

Code with Error
// File: utils.d.ts
// Trying to declare without 'declare' keyword

const API_URL = "https://api.example.com";  // ❌ Error!

function fetchData(url: string): Promise<any>;  // ❌ Error!

class HttpClient {  // ❌ Error!
  get(url: string): Promise<any>;
}

// Using in application
// app.ts
import { API_URL } from './utils';  // Can't find module!

const data = await fetchData('/users');  // fetchData not found!

❌ Initializers are not allowed in ambient contexts. An implementation cannot be declared in ambient contexts.

What's Wrong?

Declaration files (.d.ts) describe types, not implementations. Use 'declare' keyword and don't include actual values or implementations.

Corrected Code
// File: utils.d.ts
// βœ… Correct: Using 'declare' keyword

declare const API_URL: string;

declare function fetchData(url: string): Promise<any>;

declare class HttpClient {
  get(url: string): Promise<any>;
  post(url: string, data: any): Promise<any>;
}

// βœ… For module declarations
declare module 'my-utils' {
  export const API_URL: string;
  export function fetchData(url: string): Promise<any>;
  export class HttpClient {
    get(url: string): Promise<any>;
    post(url: string, data: any): Promise<any>;
  }
}

// Using in application
// app.ts
import { API_URL, fetchData, HttpClient } from 'my-utils';

const client = new HttpClient();
const data = await client.get('/users');

// βœ… For extending existing types
declare module 'express' {
  interface Request {
    user?: {
      id: number;
      email: string;
    };
  }
}

// Now you can use req.user in Express
import { Request, Response } from 'express';

function handler(req: Request, res: Response) {
  if (req.user) {
    console.log(`User: ${req.user.email}`);
  }
}

Solution: Add 'declare' keyword and remove initializers

Best Practices

  1. Use @types packages when available - don't recreate existing types
  2. Place .d.ts files in types/ folder - keep declarations organized
  3. Use declare keyword in ambient declarations
  4. Don't include implementations in .d.ts files - only types
  5. Use module augmentation to extend existing types
  6. Document complex types with JSDoc comments
  7. Keep declarations DRY - avoid duplication
  8. Version control .d.ts files - commit them to git
best-practices-example.ts
// βœ… Good: Organized type declarations
// types/global.d.ts
declare global {
  interface Window {
    customMethod(): void;
  }
}

export {};

// βœ… Good: Module declaration
// types/custom-lib.d.ts
declare module 'custom-lib' {
  export function doSomething(): void;
}

// βœ… Good: Module augmentation
// types/express.d.ts
import 'express';

declare module 'express' {
  interface Request {
    user?: { id: number };
  }
}

// βœ… Good: Documented types
/**
 * Configuration for the payment gateway
 */
declare module 'payment-lib' {
  /**
   * Initialize payment with user details
   * @param email - User's email address
   * @param amount - Amount in smallest currency unit (kobo for NGN)
   */
  export function initPayment(email: string, amount: number): void;
}

// ❌ Avoid: Including implementations
// types/utils.d.ts
declare function add(a: number, b: number): number {
  return a + b;  // ❌ No implementations!
}

// βœ… Correct:
declare function add(a: number, b: number): number;

// ❌ Avoid: Missing declare keyword
// types/globals.d.ts
const API_URL = "https://api.example.com";  // ❌ Missing 'declare'

// βœ… Correct:
declare const API_URL: string;

// ❌ Avoid: Duplicating existing types
// types/lodash.d.ts
declare module 'lodash' {
  // Don't recreate - use @types/lodash instead!
}

// βœ… Correct:
// npm install --save-dev @types/lodash

Key Takeaways

  • Use @types/package-name for existing libraries
  • declare keyword creates ambient declarations
  • .d.ts files describe types without implementations
  • Module declarations type external JavaScript libraries
  • Module augmentation extends existing module types
  • Global declarations work without imports
  • Namespace declarations organize related types
  • DefinitelyTyped has types for thousands of libraries
  • Type environment variables for better safety
  • Declaration files enable TypeScript for any JavaScript

What's Next?

Excellent work! You've mastered type declarations and can now work with any JavaScript library in TypeScript. Next, we'll explore TypeScript Best Practicesβ€”code organization, naming conventions, configuration, and patterns for writing production-ready TypeScript. This final tutorial brings everything together!

Get ready to master professional TypeScript development!

Mastering TypeScript type declarations! Check this out!

Previous
Working with APIs and JSON
Next
TypeScript Best Practices

Never Miss a New TypeScript Tutorial

Join 1,000+ developers learning TypeScript step-by-step. Get new tutorials, type safety tips, and exclusive resources delivered to your inbox - completely FREE.

You might also like

UI Components

Ready-made components for your projects

Developer Tools

Boost productivity with handy generators

HTML Game

Learn HTML concepts through interactive gameplay

Join Our Web Development Training Program

Master frontend & backend development with hands-on projects.

TypeScript Tutorials

0 of 30 completed

Your Progress0%

Topics

Getting Started

  • What is TypeScript?
  • TypeScript vs JavaScript
  • Setting Up TypeScript
  • Your First TypeScript Program

Basic Types

  • Primitive Types
  • Arrays and Tuples
  • Objects and Type Aliases
  • Enums
  • Any, Unknown, and Never

Functions & Interfaces

  • Function Types and Signatures
  • Interfaces
  • Optional and Default Parameters
  • Function Overloading

Advanced Types

  • Union and Intersection Types
  • Type Guards and Narrowing
  • Literal Types
  • Type Assertions and Casting

Generics

  • Introduction to Generics
  • Generic Constraints
  • Utility Types

Classes & OOP

  • Classes and Constructors
  • Access Modifiers
  • Inheritance and Polymorphism
  • Abstract Classes and Interfaces

Practical TypeScript

  • Working with Modules
  • TypeScript with React
  • Async/Await and Promises
  • Working with APIs and JSON
  • Type Declarations and DefinitelyTyped
  • TypeScript Best Practices
Falytom logoFALYTOM

Learn technologies like HTML, CSS, JavaScript, React, NodeJS and more through interactive games, tutorials, components and free tools. Transform your businesses with AI chatbots, SEO-optimized websites, and professional development services.

Β© 2025 Tomilola Group
  • Facebook logo
  • Twitter logo
  • Instagram logo
  • LinkedIn logo
  • GitHub logo
  • YouTube logo