WDD 430: Web Full-Stack Development

TypeScript

Overview

TypeScript Logo

TypeScript is JavaScript with helpful type checking. It runs anywhere JavaScript runs, but it can catch common mistakes before your code is executed. In Week 01, you used TypeScript in the assignment. This week, you will build on that introductory, immersion experience by understanding the advantages and essential aspects of TypeScript.

Prepare

Next.js comes with built-in TypeScript support and automatically installs the necessary packages and configurations when you start a new project.

You can add TypeScript to an existing Next.js project by renaming a file to .ts or .tsx and running next dev and next build to automatically install the necessary dependencies and configure TypeScript in a file named tsconfig.json.

TypeScript ensures that incorrect data types are not passed to components or databases.

TypeScript provides better code readability and maintainability by making it clear what types of data are expected and how they should be used. This can help developers understand the code more quickly and reduce the likelihood of bugs.

TypeScript has a large and active community, which means that there are many resources available for learning and troubleshooting. Additionally, TypeScript is widely used in the industry, so learning it can improve your job prospects as a developer.

TypeScript developers use Prisma or Drizzle, which are ORM (Object-Relational Mapping) tools for working with databases in a type-safe manner. These tools automatically generate types based on your database schema.

VS Code Support

VS Code's built-in TypeScript support handles general TypeScript features: syntax highlighting, basic type checking, and standard auto-completion for any .ts/.tsx file.

Next.js's custom TypeScript plugin is a separate, additional layer on top of that. Next.js includes a custom TypeScript plugin and type checker, which VSCode and other code editors can use for advanced type-checking and auto-completion. This plugin adds Next.js-specific intelligence, such as warning about invalid segment config options, ensuring 'use client' is used correctly, and ensuring client hooks like useState are only used in Client Components.

To enable the plugin in VS Code, open the command palette and search for "TypeScript: Select TypeScript Version" and then select Use Workspace Version.

As evidence of the active support of TypeScript in VS Code, many popular extensions have been deprecated in favor of the built-in support or covered by the ESLint extension. When running next build, the custom type checker will be used.

Examples

Here is an example of a simple TypeScript function that adds two numbers together:

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

This is a simple TypeScript function – note the : number type annotations, which are TypeScript syntax not found in plain JavaScript. The function add takes two parameters, a and b, both typed as number, and the function's return type is also declared as number.

This table summarizes some key TypeScript concepts that were applied in the Week 01 assignment.

TypeScript Mental Model
What You Built Example TypeScript Benefit
Component props for project cards
interface ProjectCardProps {
  title: string;
  description: string;
  technologies: string[];
  link?: string;
}
Defines required vs optional props so ProjectCard is used correctly.
Optional values in UI link?: string with {link && (...)} conditional rendering Prevents rendering or using a value that may be missing.
Array-driven rendering
interface ProjectListProps {
  projects: Project[];
}
and projects.map(...)
Ensures each list item has the expected shape before rendering.
Layout composition children: React.ReactNode in app/layout.tsx Guarantees layout receives valid renderable content from pages.

Activity Instructions

Fix TypeScript Errors

Fix the following TypeScript errors: Copy each snippet into a .ts file, identify the TypeScript error, and fix it.

// 1) Implicit any
function greet(name: string) {
  return `Hello, ${name.toUpperCase()}`;
}

// 2) Wrong return type (return the proper type using the reduce function using the items array)
function total(items: number[]): number {
  return "42";
}

// 3) Property does not exist on type
type Product = { id: number; title: string };
const p: Product = { id: 1, title: "Notebook" };
console.log(p.price);

// 4) Unsafe union usage
function printId(id: string | number) {
  console.log(id.toUpperCase());
}

// 5) Optional property used without a check
type User = { name: string; email?: string };
function emailDomain(user: User) {
  return user.email.split("@")[1];
}
Check Your Understanding
// 1) Implicit any
function greet(name: string): string {
  return `Hello, ${name.toUpperCase()}`;
}
// 2) Wrong return type 
function total(items: number[]): number {
  return items.reduce((sum, item) => sum + item, 0);
}
// 3) Property does not exist on type
type Product = { id: number; title: string; price: number };
const p: Product = { id: 1, title: "Notebook", price: 9.99 };
console.log(p.price);
// 4) Unsafe union usage
function printId(id: string | number) {
  if (typeof id === "string") {
    console.log(id.toUpperCase());
  } else {
    console.log(id);
  }
}
// 5) Optional property used without a check
type User = { name: string; email?: string };
function emailDomain(user: User) {
  if (user.email) {
    return user.email.split("@")[1];
  }
  return null;
}

Convert JavaScript to TypeScript

Convert this small JavaScript utility into TypeScript, then explain each type decision.

export function formatCartTotal(items, taxRate) {
  const subtotal = items.reduce((sum, item) => sum + item.price * item.qty, 0);
  const tax = subtotal * taxRate;
  return {
    subtotal,
    tax,
    total: subtotal + tax
  };
}
Check Your Understanding
interface CartItem {
  name?: string; // Optional name property for better readability
  price: number;  
  qty: number; // Expected to be a positive integer; 
}

interface CartSummary {
  subtotal: number;
  tax: number;
  total: number;
}

export function formatCartTotal(items: CartItem[], taxRate: number): CartSummary {
  const subtotal = items.reduce((sum, item) => sum + item.price * item.qty, 0);
  const tax = subtotal * taxRate;
  return {
    subtotal,
    tax,
    total: subtotal + tax
  };
}

Type Decisions Explained: