Skip to main content

Debugging, Environment & Projects

Debugging

When TypeScript shows an error, the fastest way to debug it is to inspect the type that TypeScript is currently seeing.

  • Hover over the variable, function argument, or return value in your editor.
  • Read the full error message carefully, especially the last line.
  • The last part usually tells you what TypeScript expected and what it actually received.
  • Check where the value was created, because the real issue often starts earlier than the line showing the error.

Common Debugging Pattern

function printPrice(price: string | number) {
return price.toFixed(2);
}

TypeScript will complain because toFixed() exists on number, not on string | number.

If you hover on price, you will see its type is still:

string | number

So the fix is to narrow the type first:

function printPrice(price: string | number) {
if (typeof price === "number") {
return price.toFixed(2);
}

return price;
}

@ts-ignore

// @ts-ignore
someValue.doSomething();

It ignores the error on the next line.

Use it only as a temporary escape hatch.
It hides the real type issue.

Importing Types

Sometimes you want to import something only for type checking, not for runtime use.

In that case, use import type.

Example

import type { User } from "./types";

function printUser(user: User) {
console.log(user.name);
}

This makes it clear that User is only a type.

Why Use import type?

  • It improves readability.
  • It separates runtime imports from type-only imports.
  • It is useful in larger projects where keeping imports clear matters.

Example with Normal Import

import { formatDate } from "./utils";
import type { Event } from "./types";

function printEvent(event: Event) {
console.log(formatDate(event.date));
}

Here:

  • formatDate is used at runtime
  • Event is used only for typing

Declaration Files

Declaration files tell TypeScript about types that exist somewhere else.

They usually use the .d.ts extension.

You will often see them when:

  • using JavaScript libraries
  • describing global variables
  • adding types for code that TypeScript cannot infer

Example

declare const API_URL: string;

console.log(API_URL);

Here TypeScript understands that API_URL exists and is a string.

Example: Module Declaration

declare module "my-library" {
export function greet(name: string): string;
}

This is useful when a library has no built-in TypeScript types.

Mental Model

A declaration file does not create real JavaScript code.
It only describes the shape of existing code.

Todo List Project (DOM)

In a DOM project, TypeScript helps you work safely with elements, events, and user input.

The main benefit is that TypeScript tells you what kind of DOM element you are working with.

Example

const form = document.querySelector<HTMLFormElement>("#todo-form");
const input = document.querySelector<HTMLInputElement>("#todo-input");
const list = document.querySelector<HTMLUListElement>("#todo-list");

form?.addEventListener("submit", (event) => {
event.preventDefault();

if (!input || !list || !input.value.trim()) return;

const item = document.createElement("li");
item.textContent = input.value;
list.appendChild(item);

input.value = "";
});

What TypeScript Helps With

  • knowing input.value exists
  • getting autocomplete for DOM methods
  • catching null cases from querySelector
  • making event handling safer

Common Pattern

Use DOM generics like:

document.querySelector<HTMLInputElement>("#todo-input");
document.querySelector<HTMLButtonElement>("#save-btn");

This gives you more accurate types immediately.

Migrate JS To TS Project

When migrating a JavaScript project to TypeScript, do it step by step.

Do not try to type everything perfectly on day one.

Common Migration Approach

  • rename files gradually from .js to .ts
  • start with utility files or small modules
  • add types to function parameters and return values
  • fix the most useful errors first
  • use unknown instead of any when possible

Example

JavaScript

function add(a, b) {
return a + b;
}

TypeScript

function add(a: number, b: number): number {
return a + b;
}

Practical Tips

  • Enable TypeScript and migrate file by file.
  • Add types to public functions first.
  • Use @ts-ignore only temporarily.
  • If a file is too messy, improve it in small passes.

Mental Model

Migration is not about making the project "100% typed" instantly.
It is about increasing safety gradually without breaking development speed.