Prettier and Code Formatting
Learning Objectives
- By the end of this lesson, you will be able to:
- - Understand TypeScript basics
- - Use types and interfaces
- - Add type annotations
- - Compile TypeScript to JavaScript
- - Work with TypeScript features
- - Build type-safe applications
- - Understand type system benefits
Lesson 24.2: TypeScript Introduction
Learning Objectives
By the end of this lesson, you will be able to:
- Understand TypeScript basics
- Use types and interfaces
- Add type annotations
- Compile TypeScript to JavaScript
- Work with TypeScript features
- Build type-safe applications
- Understand type system benefits
Introduction to TypeScript
TypeScript is a typed superset of JavaScript that compiles to plain JavaScript.
Why TypeScript?
- Type Safety: Catch errors at compile time
- Better IDE Support: Autocomplete, refactoring
- Documentation: Types document code
- Refactoring: Safer to refactor
- Large Projects: Better for large codebases
- Industry Standard: Widely adopted
- Modern JavaScript: Supports latest features
TypeScript Basics
Installation
# Install TypeScript
npm install -g typescript
# Or locally
npm install --save-dev typescript
# Check version
tsc --version
First TypeScript File
// hello.ts
function greet(name: string): string {
return `Hello, ${name}!`;
}
let message = greet('World');
console.log(message);
Compiling TypeScript
# Compile TypeScript
tsc hello.ts
# Generates hello.js
Types
Basic Types
// String
let name: string = 'Alice';
// Number
let age: number = 30;
// Boolean
let isActive: boolean = true;
// Array
let numbers: number[] = [1, 2, 3];
let names: Array<string> = ['Alice', 'Bob'];
// Tuple
let tuple: [string, number] = ['Alice', 30];
// Any
let anything: any = 'can be anything';
// Void
function log(message: string): void {
console.log(message);
}
// Null and Undefined
let nullValue: null = null;
let undefinedValue: undefined = undefined;
Union Types
// Union type
let value: string | number;
value = 'hello';
value = 42;
// Union with null
let name: string | null = null;
name = 'Alice';
Literal Types
// Literal types
let direction: 'up' | 'down' | 'left' | 'right';
direction = 'up';
// direction = 'diagonal'; // Error
// Boolean literal
let isTrue: true = true;
Type Annotations
Function Types
// Function with types
function add(a: number, b: number): number {
return a + b;
}
// Arrow function
let multiply = (a: number, b: number): number => {
return a * b;
};
// Function type
let operation: (a: number, b: number) => number;
operation = add;
operation = multiply;
Variable Types
// Explicit type
let count: number = 0;
// Type inference
let name = 'Alice'; // Type: string
let age = 30; // Type: number
// Type assertion
let value: any = 'hello';
let length = (value as string).length;
Object Types
// Object type
let user: {
name: string;
age: number;
email?: string; // Optional
} = {
name: 'Alice',
age: 30
};
Interfaces
Basic Interface
// Define interface
interface User {
name: string;
age: number;
email?: string; // Optional property
}
// Use interface
let user: User = {
name: 'Alice',
age: 30
};
// Function with interface
function getUser(): User {
return {
name: 'Bob',
age: 25,
email: 'bob@example.com'
};
}
Interface with Methods
interface Calculator {
add(a: number, b: number): number;
subtract(a: number, b: number): number;
}
let calc: Calculator = {
add: (a, b) => a + b,
subtract: (a, b) => a - b
};
Extending Interfaces
interface Person {
name: string;
age: number;
}
interface Employee extends Person {
employeeId: string;
department: string;
}
let employee: Employee = {
name: 'Alice',
age: 30,
employeeId: 'E001',
department: 'Engineering'
};
Classes
TypeScript Classes
class User {
name: string;
age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
greet(): string {
return `Hello, I'm ${this.name}`;
}
}
let user = new User('Alice', 30);
console.log(user.greet());
Access Modifiers
class User {
public name: string; // Public (default)
private age: number; // Private
protected email: string; // Protected
constructor(name: string, age: number, email: string) {
this.name = name;
this.age = age;
this.email = email;
}
}
Generics
Basic Generics
// Generic function
function identity<T>(arg: T): T {
return arg;
}
let number = identity<number>(42);
let string = identity<string>('hello');
// Generic interface
interface Box<T> {
value: T;
}
let numberBox: Box<number> = { value: 42 };
let stringBox: Box<string> = { value: 'hello' };
Generic Constraints
interface Lengthwise {
length: number;
}
function logLength<T extends Lengthwise>(arg: T): T {
console.log(arg.length);
return arg;
}
logLength('hello'); // OK
logLength([1, 2, 3]); // OK
// logLength(42); // Error: number doesn't have length
Compiling TypeScript
tsconfig.json
{
"compilerOptions": {
"target": "ES2020",
"module": "ES2020",
"lib": ["ES2020"],
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}
Compiler Options
{
"compilerOptions": {
"target": "ES2020", // JavaScript version
"module": "ES2020", // Module system
"lib": ["ES2020"], // Library files
"outDir": "./dist", // Output directory
"rootDir": "./src", // Root directory
"strict": true, // Strict type checking
"noImplicitAny": true, // Error on implicit any
"strictNullChecks": true, // Strict null checks
"esModuleInterop": true, // ES module interop
"skipLibCheck": true, // Skip lib checking
"sourceMap": true // Generate source maps
}
}
Compiling
# Compile TypeScript
tsc
# Watch mode
tsc --watch
# Compile specific file
tsc file.ts
# Compile with config
tsc --project tsconfig.json
Practice Exercise
Exercise: TypeScript Practice
Objective: Practice using TypeScript, working with types, interfaces, and compiling TypeScript.
Instructions:
- Install TypeScript
- Create TypeScript files
- Practice:
- Using basic types
- Creating interfaces
- Adding type annotations
- Compiling TypeScript
Example Solution:
# 1. Install TypeScript
npm install --save-dev typescript
# 2. Initialize TypeScript
npx tsc --init
// tsconfig.json
{
"compilerOptions": {
"target": "ES2020",
"module": "ES2020",
"lib": ["ES2020"],
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"sourceMap": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}
// src/basics.ts
console.log("=== TypeScript Basics ===");
// Basic types
let name: string = 'Alice';
let age: number = 30;
let isActive: boolean = true;
console.log('Name:', name);
console.log('Age:', age);
console.log('Active:', isActive);
// Arrays
let numbers: number[] = [1, 2, 3, 4, 5];
let names: Array<string> = ['Alice', 'Bob', 'Charlie'];
console.log('Numbers:', numbers);
console.log('Names:', names);
// Tuples
let tuple: [string, number] = ['Alice', 30];
console.log('Tuple:', tuple);
// Union types
let value: string | number;
value = 'hello';
console.log('Value (string):', value);
value = 42;
console.log('Value (number):', value);
// Any
let anything: any = 'can be anything';
anything = 42;
anything = true;
console.log('Anything:', anything);
// src/functions.ts
console.log("=== Functions ===");
// Function with types
function add(a: number, b: number): number {
return a + b;
}
console.log('add(2, 3):', add(2, 3));
// Arrow function
let multiply = (a: number, b: number): number => {
return a * b;
};
console.log('multiply(4, 5):', multiply(4, 5));
// Optional parameters
function greet(name: string, title?: string): string {
if (title) {
return `Hello, ${title} ${name}!`;
}
return `Hello, ${name}!`;
}
console.log('greet("Alice"):', greet('Alice'));
console.log('greet("Alice", "Dr."):', greet('Alice', 'Dr.'));
// Default parameters
function createUser(name: string, age: number = 18): { name: string; age: number } {
return { name, age };
}
console.log('createUser("Bob"):', createUser('Bob'));
console.log('createUser("Charlie", 25):', createUser('Charlie', 25));
// Rest parameters
function sum(...numbers: number[]): number {
return numbers.reduce((total, num) => total + num, 0);
}
console.log('sum(1, 2, 3, 4, 5):', sum(1, 2, 3, 4, 5));
// src/interfaces.ts
console.log("=== Interfaces ===");
// Basic interface
interface User {
name: string;
age: number;
email?: string; // Optional
}
let user: User = {
name: 'Alice',
age: 30,
email: 'alice@example.com'
};
console.log('User:', user);
// Interface with methods
interface Calculator {
add(a: number, b: number): number;
subtract(a: number, b: number): number;
multiply(a: number, b: number): number;
divide(a: number, b: number): number;
}
let calc: Calculator = {
add: (a, b) => a + b,
subtract: (a, b) => a - b,
multiply: (a, b) => a * b,
divide: (a, b) => {
if (b === 0) {
throw new Error('Division by zero');
}
return a / b;
}
};
console.log('calc.add(10, 5):', calc.add(10, 5));
console.log('calc.subtract(10, 5):', calc.subtract(10, 5));
console.log('calc.multiply(10, 5):', calc.multiply(10, 5));
console.log('calc.divide(10, 5):', calc.divide(10, 5));
// Extending interfaces
interface Person {
name: string;
age: number;
}
interface Employee extends Person {
employeeId: string;
department: string;
}
let employee: Employee = {
name: 'Bob',
age: 25,
employeeId: 'E001',
department: 'Engineering'
};
console.log('Employee:', employee);
// src/classes.ts
console.log("=== Classes ===");
class User {
name: string;
age: number;
private email: string;
constructor(name: string, age: number, email: string) {
this.name = name;
this.age = age;
this.email = email;
}
greet(): string {
return `Hello, I'm ${this.name}`;
}
getEmail(): string {
return this.email;
}
}
let user = new User('Alice', 30, 'alice@example.com');
console.log('User:', user.greet());
console.log('Email:', user.getEmail());
// Class with inheritance
class Admin extends User {
permissions: string[];
constructor(name: string, age: number, email: string, permissions: string[]) {
super(name, age, email);
this.permissions = permissions;
}
hasPermission(permission: string): boolean {
return this.permissions.includes(permission);
}
}
let admin = new Admin('Admin', 35, 'admin@example.com', ['read', 'write', 'delete']);
console.log('Admin:', admin.greet());
console.log('Has write permission:', admin.hasPermission('write'));
// src/generics.ts
console.log("=== Generics ===");
// Generic function
function identity<T>(arg: T): T {
return arg;
}
let number = identity<number>(42);
let string = identity<string>('hello');
let boolean = identity<boolean>(true);
console.log('Identity number:', number);
console.log('Identity string:', string);
console.log('Identity boolean:', boolean);
// Generic interface
interface Box<T> {
value: T;
getValue(): T;
}
let numberBox: Box<number> = {
value: 42,
getValue: function() {
return this.value;
}
};
let stringBox: Box<string> = {
value: 'hello',
getValue: function() {
return this.value;
}
};
console.log('Number box:', numberBox.getValue());
console.log('String box:', stringBox.getValue());
// Generic class
class Container<T> {
private items: T[] = [];
add(item: T): void {
this.items.push(item);
}
get(index: number): T {
return this.items[index];
}
getAll(): T[] {
return this.items;
}
}
let numberContainer = new Container<number>();
numberContainer.add(1);
numberContainer.add(2);
numberContainer.add(3);
console.log('Number container:', numberContainer.getAll());
let stringContainer = new Container<string>();
stringContainer.add('a');
stringContainer.add('b');
stringContainer.add('c');
console.log('String container:', stringContainer.getAll());
// src/index.ts
import './basics';
import './functions';
import './interfaces';
import './classes';
import './generics';
// package.json
{
"name": "typescript-practice",
"version": "1.0.0",
"scripts": {
"build": "tsc",
"watch": "tsc --watch",
"start": "node dist/index.js"
},
"devDependencies": {
"typescript": "^5.2.0"
}
}
Expected Output (after compiling and running):
=== TypeScript Basics ===
Name: Alice
Age: 30
Active: true
Numbers: [1, 2, 3, 4, 5]
Names: ['Alice', 'Bob', 'Charlie']
Tuple: ['Alice', 30]
Value (string): hello
Value (number): 42
Anything: true
=== Functions ===
add(2, 3): 5
multiply(4, 5): 20
greet("Alice"): Hello, Alice!
greet("Alice", "Dr."): Hello, Dr. Alice!
createUser("Bob"): { name: 'Bob', age: 18 }
createUser("Charlie", 25): { name: 'Charlie', age: 25 }
sum(1, 2, 3, 4, 5): 15
=== Interfaces ===
User: { name: 'Alice', age: 30, email: 'alice@example.com' }
calc.add(10, 5): 15
calc.subtract(10, 5): 5
calc.multiply(10, 5): 50
calc.divide(10, 5): 2
Employee: { name: 'Bob', age: 25, employeeId: 'E001', department: 'Engineering' }
=== Classes ===
User: Hello, I'm Alice
Email: alice@example.com
Admin: Hello, I'm Admin
Has write permission: true
=== Generics ===
Identity number: 42
Identity string: hello
Identity boolean: true
Number box: 42
String box: hello
Number container: [1, 2, 3]
String container: ['a', 'b', 'c']
Challenge (Optional):
- Convert JavaScript project to TypeScript
- Add types to existing code
- Create type definitions
- Build a complete TypeScript application
Common Mistakes
1. Using any Too Much
// ❌ Bad: Too much any
function process(data: any): any {
return data;
}
// ✅ Good: Use specific types
function process<T>(data: T): T {
return data;
}
2. Not Using Strict Mode
// ❌ Bad: No strict mode
{
"compilerOptions": {
"strict": false
}
}
// ✅ Good: Use strict mode
{
"compilerOptions": {
"strict": true
}
}
3. Ignoring Type Errors
// ❌ Bad: Ignore errors
// @ts-ignore
let value = undefinedValue.property;
// ✅ Good: Fix the error
let value = definedValue?.property;
Key Takeaways
- TypeScript: Typed superset of JavaScript
- Types: string, number, boolean, arrays, etc.
- Interfaces: Define object shapes
- Classes: Type-safe classes with modifiers
- Generics: Reusable type-safe code
- Compilation: Compiles to JavaScript
- Best Practice: Use strict mode, avoid any, leverage types
Quiz: TypeScript
Test your understanding with these questions:
-
TypeScript is:
- A) Typed superset of JavaScript
- B) Different language
- C) Both
- D) Neither
-
Type annotation uses:
- A) :
- B) =
- C) Both
- D) Neither
-
Interface defines:
- A) Object shape
- B) Function
- C) Both
- D) Neither
-
private modifier:
- A) Accessible anywhere
- B) Accessible in class only
- C) Accessible in subclasses
- D) Nothing
-
Generics use:
- A) <T>
- B) {T}
- C) [T]
- D) (T)
-
tsc compiles:
- A) TypeScript to JavaScript
- B) JavaScript to TypeScript
- C) Both
- D) Neither
-
strict mode:
- A) Enables strict checking
- B) Disables checking
- C) Both
- D) Neither
Answers:
- A) Typed superset of JavaScript
- A) :
- A) Object shape
- B) Accessible in class only
- A) <T>
- A) TypeScript to JavaScript
- A) Enables strict checking
Next Steps
Congratulations! You've completed Module 24: Code Quality and Linting. You now know:
- ESLint for code quality
- TypeScript for type safety
- How to maintain code quality
- How to build type-safe applications
What's Next?
- Course 7: Frontend Frameworks
- Module 25: React Basics
- Lesson 25.1: React Introduction
- Learn what React is
- Set up React projects
Additional Resources
- TypeScript Documentation: typescriptlang.org
- TypeScript Handbook: typescriptlang.org/docs/handbook/intro.html
- TypeScript Playground: typescriptlang.org/play
Lesson completed! You've finished Module 24: Code Quality and Linting. Ready for Course 7: Frontend Frameworks!
Course Navigation
Build Tools and Bundlers
Build Tools and Bundlers