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:
formatDateis used at runtimeEventis 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.valueexists - getting autocomplete for DOM methods
- catching
nullcases fromquerySelector - 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
.jsto.ts - start with utility files or small modules
- add types to function parameters and return values
- fix the most useful errors first
- use
unknowninstead ofanywhen 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-ignoreonly 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.