TypeScript
Overview
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.
- Parameters:
a: numberandb: number– TypeScript will reject any non-numeric argument at compile time. - Return:
: numberafter the parentheses declares the return type. TypeScript will error if the function returns anything else. - Type Safety: TypeScript's type system helps catch errors at compile time, improving code
quality and reducing runtime errors. Example: If you call
add("hello", 2), TypeScript throws a compile-time error – catching the error before it can cause issues at runtime.
This table summarizes some key TypeScript concepts that were applied in the Week 01 assignment.
| What You Built | Example | TypeScript Benefit |
|---|---|---|
| Component props for project cards |
|
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 |
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:
CartIteminterface defines the expected shape of each item in the cart, ensuring that each item has a numeric price and quantity.Interfaces are open for extension, allowing you to add new properties later without breaking existing code.
CartSummaryinterface defines the structure of the returned summary object, making it clear what properties to expect and their types.formatCartTotalfunction signature specifies that it takes an array ofCartItemobjects and a numeric tax rate, and returns aCartSummary, providing clear expectations for inputs and outputs.