Skip to Content
Course content

5: TypeScript in VS Code

Click on the "Edit" button in the top corner of the screen to edit your slide content.

If you've used VS Code for JavaScript, you already know it's a great editor. But when you move to TypeScript, there is a massive difference between using VS Code as a fancy text editor and using it as a full-blown development environment. I see this a lot with developers moving from other languages; they treat the IDE as a place to type, and the terminal as the place where the "actual" work of checking types happens.

The "Run and Pray" Workflow

The naive way to approach this is to just create a .ts file, write some code, and run tsc or ts-node in your terminal to see if it works. Imagine you're building a small system to manage a library's book inventory. You've got a Book interface and a function to check if a book is overdue. In the naive workflow, you write your logic, hit save, switch to the terminal, run the compiler, and then—if you're lucky—you get a list of errors that you then have to map back to the line numbers in your code.

// bookManager.ts
interface Book {
    title: string;
    dueDate: Date;
}

function isOverdue(book: Book) {
    return book.dueDate < new Date();
}

const myBook = { title: "The TypeScript Guide", due_date: new Date() }; 
isOverdue(myBook); // You don't notice the typo 'due_date' until you run the compiler.

This approach costs you cognitive load. You're constantly context-switching between your code and your terminal. It breaks the moment your project grows beyond a single file because you're manually managing the compilation of individual files rather than treating the project as a cohesive unit.

Bridging the Gap with tsconfig.json

The better way is to realize that VS Code isn't just displaying your code—it's running a TypeScript language server in the background. The secret to making this server actually useful is the tsconfig.json file. Without it, VS Code makes a lot of guesses about how to treat your files, and those guesses often differ from how the tsc command-line tool behaves. This is where the "it works in my editor but fails in the build" frustration comes from.

When you add a tsconfig.json with "strict": true, you're essentially telling VS Code: "Stop being polite and start being pedantic." Suddenly, those red squiggles appear the millisecond you make a mistake. In the library example above, the moment you typed due_date instead of dueDate, VS Code would have flagged it. I usually tell my colleagues that if you're spending more than five seconds looking at your terminal to find a type error, you're using the tool wrong.

Navigating the Type Graph

Once you have the environment synced, you can stop reading your code linearly. In a real-world project, types can get deeply nested. Instead of scrolling up 200 lines to remember what properties a UserSession object has, you can just Cmd/Ctrl-click the type name. This "Go to Definition" feature is the single biggest productivity boost in TypeScript. I've spent hours of my career just jumping between definitions to trace how data flows through a system.

I also highly recommend getting comfortable with the "Quick Fix" menu (Cmd+. or Ctrl+.). If you have a type mismatch, don't just manually rewrite the variable. Use the Quick Fix to see if TypeScript can suggest a more appropriate type or a way to cast the value. It's a conversation with the compiler rather than a fight against it. You're moving from a cycle of "Write -> Compile -> Fix" to a real-time loop of "Write -> Correct -> Move On."




📋 Practical Task

Fixing the Library Inventory Type Mismatches

You have been handed a small project for a library system, but it's riddled with type errors that the compiler is complaining about. Your task is to use the VS Code "Problems" tab and "Quick Fix" features to resolve these issues without breaking the logic.

Setup: Create a folder with a tsconfig.json containing {"compilerOptions": {"strict": true}} and a file named inventory.ts with the following code:

interface Member {
    id: string;
    name: string;
    membershipLevel: 'gold' | 'silver' | 'bronze';
}

interface Loan {
    bookTitle: string;
    memberId: string;
    returnDate: Date | null;
}

function getMemberStatus(member: Member) {
    // ERROR: Logic requires a string, but membershipLevel is a union
    return `Member ${member.name} is ${member.membershipLevel.toUpperCase()}`;
}

const activeLoans: Loan[] = [
    { bookTitle: "TypeScript Deep Dive", memberId: "M123", returnDate: new Date() },
    { bookTitle: "Clean Code", memberId: "M456", returnDate: null }
];

function processReturn(loan: Loan) {
    // ERROR: returnDate could be null
    console.log("Processing return for: " + loan.returnDate.toDateString());
}

const newMember = { id: "M789", name: "Alice", membershipLevel: "platinum" };
getMemberStatus(newMember); // ERROR: 'platinum' is not a valid membershipLevel

Your Goal:

  • Fix the newMember object so it matches the Member interface.
  • Handle the potential null value in processReturn using a type guard or optional chaining so the toDateString() call is safe.
  • Verify that the "Problems" tab in VS Code is empty before saving.
Rating
0 0

There are no comments for now.

to be the first to leave a comment.