Now that you have TypeScript installed and configured, it's time to write your first TypeScript program! You'll learn the complete development workflow: writing TypeScript code, compiling it to JavaScript, and running it. Let's dive in!
Hello World in TypeScript
Let's start with the classic "Hello World" program to understand the basics:
Step 1: Create the TypeScript File
Create a new file called hello.ts in your src folder:
// Your first TypeScript program!
const message: string = "Hello, TypeScript World!";
console.log(message);π File Extension
TypeScript files use the .ts extension. For React/JSX code, use .tsx extension instead.
Step 2: Compile the TypeScript Code
Use the TypeScript compiler to convert your .ts file to JavaScript:
# Compile the TypeScript file
tsc src/hello.ts
# Or if using project setup from previous lesson
npm run buildThis creates a hello.js file in your dist folder (or same directory if not using tsconfig.json).
Step 3: View the Compiled JavaScript
Let's see what TypeScript compiled to:
// Compiled JavaScript (types removed)
"use strict";
var message = "Hello, TypeScript World!";
console.log(message);Notice that the type annotation (: string) has been removed! Types only exist during developmentβthe final JavaScript is clean and runs anywhere.
Step 4: Run the JavaScript
Execute the compiled JavaScript file using Node.js:
# Run the compiled JavaScript
node dist/hello.js
# Output:
# Hello, TypeScript World!Congratulations! You just wrote, compiled, and ran your first TypeScript program. This is the fundamental TypeScript workflow you'll use for every project.
The TypeScript Development Workflow
Understanding the complete workflow is essential for productive TypeScript development:
- Write TypeScript code (
.tsfiles) with type annotations - TypeScript compiler checks types and shows errors in your editor
- Fix any type errors before compilation
- Compile to JavaScript using
tsccommand - Run the JavaScript in Node.js or browser
β‘ Pro Tip: Watch Mode
Use tsc --watch or npm run build:watch to automatically recompile whenever you save files. This eliminates step 4 from your workflow!
Working with Basic Types
Let's expand our program to use different types:
// Working with different types
// String type
const userName: string = "Yusuf";
const greeting: string = `Hello, ${userName}!`;
// Number type
const age: number = 25;
const price: number = 99.99;
// Boolean type
const isStudent: boolean = true;
const hasLicense: boolean = false;
// Array types
const fruits: string[] = ["Apple", "Banana", "Orange"];
const numbers: number[] = [1, 2, 3, 4, 5];
// Output everything
console.log(greeting);
console.log(`Age: ${age}`);
console.log(`Price: β¦${price}`);
console.log(`Is student: ${isStudent}`);
console.log(`Fruits: ${fruits.join(", ")}`);
console.log(`Sum: ${numbers.reduce((a, b) => a + b, 0)}`);Compile and Run
# Compile
tsc src/basics.ts
# Run
node dist/basics.js
# Output:
# Hello, Yusuf!
# Age: 25
# Price: β¦99.99
# Is student: true
# Fruits: Apple, Banana, Orange
# Sum: 15Type annotations like : string, : number, and : boolean tell TypeScript what kind of data each variable should hold. We'll explore types in detail in the next lessons!
Functions with Type Annotations
Functions are where TypeScript really shines. Let's see the difference between JavaScript and TypeScript:
Functions: JavaScript vs TypeScript
// JavaScript - No types
function greet(name) {
return "Hello, " + name;
}
const message = greet("World");
console.log(message);
// These all "work" but may cause issues
greet(123);
greet();
greet(null);// TypeScript - Type-safe
function greet(name: string): string {
return "Hello, " + name;
}
const message: string = greet("World");
console.log(message);
// TypeScript prevents errors
greet(123); // β Error: number not assignable
greet(); // β Error: Expected 1 argument
greet(null); // β Error: null not assignableWhy TypeScript is Better: TypeScript adds type annotations (: string) that ensure functions are called correctly and catch errors during development.
Key TypeScript Benefits
- Type safety catches errors before runtime
- Better IDE autocomplete and IntelliSense
- Self-documenting code through types
- Easier refactoring and maintenance
- Fewer runtime errors in production
Creating Type-Safe Functions
Here's a complete example with multiple functions:
// Function with parameter and return types
function add(a: number, b: number): number {
return a + b;
}
// Function with string parameters
function createFullName(firstName: string, lastName: string): string {
return `${firstName} ${lastName}`;
}
// Function that returns nothing (void)
function logMessage(message: string): void {
console.log(`[LOG]: ${message}`);
}
// Function with boolean return
function isAdult(age: number): boolean {
return age >= 18;
}
// Using the functions
const sum: number = add(10, 20);
const fullName: string = createFullName("Yusuf", "Ibrahim");
const canVote: boolean = isAdult(25);
logMessage("Testing TypeScript functions");
console.log(`Sum: ${sum}`);
console.log(`Full Name: ${fullName}`);
console.log(`Can vote: ${canVote}`);
// These will cause TypeScript errors:
// add("10", "20"); // β Error: string not assignable to number
// isAdult("twenty-five"); // β Error: string not assignable to number
// logMessage(123); // β Error: number not assignable to stringUnderstanding Function Type Syntax
function functionName(param1: Type1, param2: Type2): ReturnType {
// Function body
return value;
}
// Example breakdown:
function greet(name: string): string {
// β β β β
// | | | βββ Return type
// | | βββββββββββ Parameter type
// | βββββββββββββββββ Parameter name
// ββββββββββββββββββββββββ Function name
return `Hello, ${name}`;
}Practical Example: Calculator Program
Let's build a simple calculator to practice what we've learned:
// Simple calculator with TypeScript
// Addition function
function add(x: number, y: number): number {
return x + y;
}
// Subtraction function
function subtract(x: number, y: number): number {
return x - y;
}
// Multiplication function
function multiply(x: number, y: number): number {
return x * y;
}
// Division function with error handling
function divide(x: number, y: number): number {
if (y === 0) {
throw new Error("Cannot divide by zero!");
}
return x / y;
}
// Calculate function that uses a string operator
function calculate(x: number, operator: string, y: number): number {
switch (operator) {
case "+":
return add(x, y);
case "-":
return subtract(x, y);
case "*":
return multiply(x, y);
case "/":
return divide(x, y);
default:
throw new Error(`Unknown operator: ${operator}`);
}
}
// Using the calculator
console.log("=== TypeScript Calculator ===");
console.log(`10 + 5 = ${calculate(10, "+", 5)}`);
console.log(`10 - 5 = ${calculate(10, "-", 5)}`);
console.log(`10 * 5 = ${calculate(10, "*", 5)}`);
console.log(`10 / 5 = ${calculate(10, "/", 5)}`);
// This will cause a runtime error (not a type error)
try {
console.log(`10 / 0 = ${calculate(10, "/", 0)}`);
} catch (error) {
console.error("Error:", (error as Error).message);
}
// These will cause TypeScript compile errors:
// calculate("10", "+", "5"); // β Error: strings not numbers
// calculate(10, "+"); // β Error: missing argument
// add(10, "5"); // β Error: string not numberCompile and Run the Calculator
# Compile
tsc src/calculator.ts
# Run
node dist/calculator.js
# Output:
# === TypeScript Calculator ===
# 10 + 5 = 15
# 10 - 5 = 5
# 10 * 5 = 50
# 10 / 5 = 2
# Error: Cannot divide by zero!π― Key Learning Points
- Functions must specify parameter types and return types
- TypeScript catches type errors before runtime
- Logic errors (like dividing by zero) still happen at runtime
- Type safety makes code more predictable and maintainable
Type Inference: TypeScript's Smart Feature
TypeScript can often figure out types automatically without explicit annotations:
// TypeScript infers the type automatically
// TypeScript knows this is a string
let message = "Hello"; // Inferred as string
message = "World"; // β
Works
// message = 123; // β Error: number not assignable to string
// TypeScript knows this is a number
let count = 10; // Inferred as number
count = 20; // β
Works
// count = "twenty"; // β Error: string not assignable to number
// TypeScript infers array type
let fruits = ["Apple", "Banana"]; // Inferred as string[]
fruits.push("Orange"); // β
Works
// fruits.push(123); // β Error: number not in string[]
// TypeScript infers function return type
function double(x: number) { // Return type inferred as number
return x * 2;
}
const result = double(5); // TypeScript knows result is number
// Explicit types vs inference
let explicitName: string = "Yusuf"; // Explicit type
let inferredName = "Yusuf"; // Inferred type (same result!)
console.log(`Message: ${message}`);
console.log(`Count: ${count}`);
console.log(`Fruits: ${fruits.join(", ")}`);
console.log(`Double of 5: ${result}`);Best Practice: Let TypeScript infer types when obvious (like let x = 5). Add explicit types for function parameters and return values, or when the inference might be unclear.
Understanding TypeScript Errors
When you make a type mistake, TypeScript shows helpful error messages:
// Example with intentional errors
function greet(name: string): string {
return `Hello, ${name}`;
}
// Error 1: Wrong type argument
greet(123);
// β Error: Argument of type 'number' is not assignable to parameter of type 'string'
// Error 2: Missing argument
greet();
// β Error: Expected 1 arguments, but got 0
// Error 3: Wrong type assignment
let age: number = "twenty-five";
// β Error: Type 'string' is not assignable to type 'number'
// Error 4: Wrong array element type
let numbers: number[] = [1, 2, 3];
numbers.push("four");
// β Error: Argument of type 'string' is not assignable to parameter of type 'number'
// Error 5: Calling wrong method
let count: number = 10;
count.toUpperCase();
// β Error: Property 'toUpperCase' does not exist on type 'number'π Reading TypeScript Errors
TypeScript errors are very descriptive:
- They tell you exactly what's wrong
- They show the line number
- They suggest what type was expected
- Your editor highlights the error location
Don't be intimidatedβread the error message carefully!
Complete Example: Student Grade Calculator
Let's combine everything into a practical program:
// Student Grade Calculator
// Define types for our data
interface Student {
name: string;
scores: number[];
}
// Calculate average score
function calculateAverage(scores: number[]): number {
const sum = scores.reduce((total, score) => total + score, 0);
return sum / scores.length;
}
// Determine grade letter
function getGrade(average: number): string {
if (average >= 90) return "A";
if (average >= 80) return "B";
if (average >= 70) return "C";
if (average >= 60) return "D";
return "F";
}
// Check if student passed
function isPassing(average: number): boolean {
return average >= 60;
}
// Generate report for a student
function generateReport(student: Student): void {
const average: number = calculateAverage(student.scores);
const grade: string = getGrade(average);
const status: string = isPassing(average) ? "PASSED" : "FAILED";
console.log(`
=== Report for ${student.name} ===`);
console.log(`Scores: ${student.scores.join(", ")}`);
console.log(`Average: ${average.toFixed(2)}`);
console.log(`Grade: ${grade}`);
console.log(`Status: ${status}`);
}
// Create students
const student1: Student = {
name: "Yusuf Ibrahim",
scores: [85, 90, 78, 92, 88]
};
const student2: Student = {
name: "Amina Mohammed",
scores: [95, 98, 92, 96, 94]
};
const student3: Student = {
name: "Chidi Okafor",
scores: [65, 58, 72, 60, 55]
};
// Generate reports
console.log("π Student Grade Reports");
generateReport(student1);
generateReport(student2);
generateReport(student3);
// These would cause TypeScript errors:
// const badStudent: Student = { name: "Test" }; // β Missing scores
// generateReport("Not a student"); // β Wrong type
// calculateAverage(["80", "90"]); // β String array instead of number arrayRun the Complete Program
# Compile
tsc src/grades.ts
# Run
node dist/grades.js
# Output:
# π Student Grade Reports
#
# === Report for Yusuf Ibrahim ===
# Scores: 85, 90, 78, 92, 88
# Average: 86.60
# Grade: B
# Status: PASSED
#
# === Report for Amina Mohammed ===
# Scores: 95, 98, 92, 96, 94
# Average: 95.00
# Grade: A
# Status: PASSED
#
# === Report for Chidi Okafor ===
# Scores: 65, 58, 72, 60, 55
# Average: 62.00
# Grade: D
# Status: PASSEDCommon Beginner Mistakes
Mistake 1: Forgetting to Compile
Problem: Running .ts files directly with Node.js
Solution: Always compile first with tsc, then run the .js file
# β Wrong
node src/hello.ts # Node.js can't run TypeScript!
# β
Correct
tsc src/hello.ts # Compile first
node dist/hello.js # Run the JavaScriptMistake 2: Ignoring Type Errors
Problem: Code compiles even with type errors
Solution: Fix all type errors before running. Use "noEmitOnError": true in tsconfig.json to prevent compilation with errors
Mistake 3: Using any Type Everywhere
Problem: Using any defeats the purpose of TypeScript
Solution: Use specific types. Only use any when absolutely necessary
// β Bad - Using any everywhere
function process(data: any): any {
return data;
}
// β
Good - Using specific types
function process(data: string): string {
return data.toUpperCase();
}Mistake 4: Not Understanding Compilation
π‘ Remember
- TypeScript (
.ts) β Compile β JavaScript (.js) - Browsers and Node.js run JavaScript, not TypeScript
- Types exist only during development
- The compiled JavaScript has no type annotations
Development Tips
Tip 1: Use Watch Mode
Save time by automatically recompiling on file changes:
# Start watch mode
tsc --watch
# Or using npm script
npm run build:watch
# Now TypeScript recompiles automatically when you save!Tip 2: Check Compiled Output
Occasionally review the compiled JavaScript to understand what TypeScript generates:
# View compiled JavaScript
cat dist/hello.js
# Or open in your editor
code dist/hello.jsTip 3: Use Your Editor's TypeScript Features
- Hover over variables to see their inferred types
- Use autocomplete (Ctrl+Space) for suggestions
- Right-click β Go to Definition to see type definitions
- Use F2 to rename symbols safely across files
Tip 4: Start Simple
When learning, start with simple programs and gradually add complexity. Don't try to use advanced TypeScript features before mastering the basics!
Key Takeaways
- TypeScript files use
.tsextension - Write code β Compile with
tscβ Run JavaScript withnode - Type annotations use syntax:
variable: type - Functions specify parameter types and return types
- TypeScript can infer types automatically in many cases
- Type errors are caught during compilation, not runtime
- Use
tsc --watchfor automatic recompilation - Read error messages carefullyβthey're very helpful!
- Compiled JavaScript has no types (they're removed)
- Let your editor help you with autocomplete and type checking
What's Next?
Congratulations! You've written several TypeScript programs and understand the development workflow. In the next lesson, we'll dive deep into Primitive Typesβexploring string, number, boolean, null, and undefined types in detail. You'll learn how to use each type effectively and when to choose one over another.
Get ready to master TypeScript's type system from the ground up!