TypeScript Nedir?
TypeScript, Microsoft tarafından geliştirilen, JavaScript'e static type checking ekleyen bir programlama dilidir. JavaScript'in tüm özelliklerini içerir ve compile-time'da type checking yaparak runtime hatalarını önler.
TypeScript'in Avantajları
- Type Safety: Compile-time hata yakalama
- Better IDE Support: IntelliSense, refactoring, navigation
- Early Bug Detection: Development sürecinde hata yakalama
- Better Documentation: Kod kendi kendini dokümante eder
- Refactoring Support: Güvenli kod değişiklikleri
- ES6+ Features: Modern JavaScript özelliklerini destekler
1. TypeScript Kurulumu ve Konfigürasyonu
Global Kurulum
# TypeScript compiler'ı global olarak kur
npm install -g typescript
# Version kontrol
tsc --version
# TypeScript dosyasını compile et
tsc hello.ts
# Watch mode
tsc hello.ts --watch
Proje Kurulumu
# Yeni Node.js projesi
mkdir my-ts-project
cd my-ts-project
npm init -y
# TypeScript development dependency olarak kur
npm install -D typescript @types/node
# tsconfig.json oluştur
npx tsc --init
# Development server için
npm install -D ts-node nodemon
tsconfig.json Konfigürasyonu
{
"compilerOptions": {
"target": "ES2020",
"module": "commonjs",
"lib": ["ES2020", "DOM"],
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"allowSyntheticDefaultImports": true,
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"noImplicitAny": true,
"noImplicitReturns": true,
"noImplicitThis": true,
"noUnusedLocals": true,
"noUnusedParameters": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}
2. Temel Type System
Primitive Types
// String
let message: string = "Hello, TypeScript!";
let template: string = `Welcome ${message}`;
// Number
let integer: number = 42;
let float: number = 3.14;
let hex: number = 0xff;
let binary: number = 0b1010;
// Boolean
let isActive: boolean = true;
let isCompleted: boolean = false;
// Null ve Undefined
let nullValue: null = null;
let undefinedValue: undefined = undefined;
// Any (TypeScript avantajlarını kaybedersiniz)
let anything: any = 42;
anything = "string";
anything = true;
// Unknown (type-safe any)
let userInput: unknown;
userInput = 5;
userInput = "text";
if (typeof userInput === "string") {
console.log(userInput.toUpperCase()); // Type guard gerekli
}
// Void (genellikle function return type)
function logMessage(): void {
console.log("This function returns nothing");
}
// Never (asla return etmeyen functions)
function throwError(message: string): never {
throw new Error(message);
}
Arrays ve Tuples
// Arrays
let numbers: number[] = [1, 2, 3, 4, 5];
let strings: Array<string> = ["a", "b", "c"];
let mixed: (string | number)[] = [1, "two", 3, "four"];
// Readonly arrays
let readonlyNumbers: readonly number[] = [1, 2, 3];
// readonlyNumbers.push(4); // Error!
// Tuples (fixed length arrays with specific types)
let person: [string, number] = ["John", 30];
let coordinates: [number, number, number] = [10, 20, 30];
// Named tuples
let namedTuple: [name: string, age: number] = ["Alice", 25];
// Optional tuple elements
let optionalTuple: [string, number?] = ["Bob"];
// Rest in tuples
let restTuple: [string, ...number[]] = ["values", 1, 2, 3, 4];
3. Object Types ve Interfaces
Object Type Annotations
// Inline object type
let user: { name: string; age: number; email?: string } = {
name: "John Doe",
age: 30
};
// Type alias
type User = {
id: number;
name: string;
email: string;
isActive?: boolean; // Optional property
readonly createdAt: Date; // Read-only property
};
let newUser: User = {
id: 1,
name: "Jane Smith",
email: "jane@example.com",
createdAt: new Date()
};
// newUser.createdAt = new Date(); // Error: readonly property
Interfaces
// Interface definition
interface Product {
id: number;
name: string;
price: number;
category: string;
description?: string;
readonly sku: string;
}
// Interface implementation
const laptop: Product = {
id: 1,
name: "MacBook Pro",
price: 2499,
category: "Electronics",
sku: "MBP-001"
};
// Interface extending
interface ElectronicProduct extends Product {
warranty: number;
brand: string;
}
// Multiple interface extending
interface SmartDevice extends Product {
connectivity: string[];
batteryLife?: number;
}
interface SmartPhone extends ElectronicProduct, SmartDevice {
screenSize: number;
operatingSystem: string;
}
// Interface for functions
interface Calculator {
(a: number, b: number): number;
}
const add: Calculator = (x, y) => x + y;
const multiply: Calculator = (x, y) => x * y;
Index Signatures
// Index signature for objects with dynamic keys
interface StringDictionary {
[key: string]: string;
}
let config: StringDictionary = {
apiUrl: "https://api.example.com",
version: "1.0.0",
environment: "production"
};
// Mixed index signature
interface MixedDictionary {
[key: string]: string | number | boolean;
// specific properties can also be defined
name: string;
count: number;
}
// Numeric index signature
interface NumberArray {
[index: number]: string;
length: number;
}
4. Functions ve Type Signatures
Function Type Annotations
// Function declaration
function greet(name: string): string {
return `Hello, ${name}!`;
}
// Function expression
const multiply = function(a: number, b: number): number {
return a * b;
};
// Arrow function
const divide = (a: number, b: number): number => a / b;
// Optional parameters
function buildName(firstName: string, lastName?: string): string {
return lastName ? `${firstName} ${lastName}` : firstName;
}
// Default parameters
function createUser(name: string, age: number = 18): User {
return { name, age };
}
// Rest parameters
function sum(...numbers: number[]): number {
return numbers.reduce((total, num) => total + num, 0);
}
// Function overloads
function format(value: string): string;
function format(value: number): string;
function format(value: boolean): string;
function format(value: string | number | boolean): string {
return String(value);
}
Higher-Order Functions
// Function as parameter
function processArray<T>(
arr: T[],
callback: (item: T, index: number) => T
): T[] {
return arr.map(callback);
}
// Function as return type
function createCounter(): () => number {
let count = 0;
return () => ++count;
}
const counter = createCounter();
console.log(counter()); // 1
console.log(counter()); // 2
// Async functions
async function fetchUser(id: number): Promise<User> {
const response = await fetch(`/api/users/${id}`);
return response.json();
}
// Generic function
function identity<T>(arg: T): T {
return arg;
}
let output1 = identity<string>("hello");
let output2 = identity<number>(42);
5. Union Types ve Type Guards
Union Types
// Union type
type Status = "loading" | "success" | "error";
type ID = string | number;
function processId(id: ID): string {
// Type narrowing required
if (typeof id === "string") {
return id.toUpperCase();
}
return id.toString();
}
// Discriminated unions
type LoadingState = { status: "loading" };
type SuccessState = { status: "success"; data: any };
type ErrorState = { status: "error"; message: string };
type ApiState = LoadingState | SuccessState | ErrorState;
function handleApiState(state: ApiState) {
switch (state.status) {
case "loading":
console.log("Loading...");
break;
case "success":
console.log("Data:", state.data);
break;
case "error":
console.log("Error:", state.message);
break;
default:
// Exhaustiveness check
const _exhaustive: never = state;
return _exhaustive;
}
}
Type Guards
// typeof type guard
function processValue(value: string | number): string {
if (typeof value === "string") {
return value.toUpperCase(); // TypeScript knows it's string
}
return value.toFixed(2); // TypeScript knows it's number
}
// instanceof type guard
class Cat {
meow() { console.log("Meow!"); }
}
class Dog {
bark() { console.log("Woof!"); }
}
function makeSound(animal: Cat | Dog) {
if (animal instanceof Cat) {
animal.meow(); // TypeScript knows it's Cat
} else {
animal.bark(); // TypeScript knows it's Dog
}
}
// Custom type guard
interface Fish {
swim(): void;
}
interface Bird {
fly(): void;
}
function isFish(pet: Fish | Bird): pet is Fish {
return (pet as Fish).swim !== undefined;
}
function move(pet: Fish | Bird) {
if (isFish(pet)) {
pet.swim(); // TypeScript knows it's Fish
} else {
pet.fly(); // TypeScript knows it's Bird
}
}
// 'in' operator type guard
type Vehicle = { drive(): void } | { fly(): void };
function operate(vehicle: Vehicle) {
if ("drive" in vehicle) {
vehicle.drive();
} else {
vehicle.fly();
}
}
6. Generics
Generic Functions
// Basic generic function
function identity<T>(arg: T): T {
return arg;
}
// Generic with constraints
interface Lengthwise {
length: number;
}
function logAndReturn<T extends Lengthwise>(arg: T): T {
console.log(arg.length);
return arg;
}
// Multiple generic parameters
function merge<T, U>(obj1: T, obj2: U): T & U {
return { ...obj1, ...obj2 };
}
const merged = merge({ name: "John" }, { age: 30 });
// merged has type { name: string } & { age: number }
// Generic with keyof
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
const person = { name: "Alice", age: 30, city: "New York" };
const name = getProperty(person, "name"); // type: string
const age = getProperty(person, "age"); // type: number
Generic Classes
// Generic class
class Container<T> {
private contents: T[] = [];
add(item: T): void {
this.contents.push(item);
}
get(index: number): T | undefined {
return this.contents[index];
}
getAll(): T[] {
return [...this.contents];
}
size(): number {
return this.contents.length;
}
}
const stringContainer = new Container<string>();
stringContainer.add("hello");
stringContainer.add("world");
const numberContainer = new Container<number>();
numberContainer.add(1);
numberContainer.add(2);
// Generic interface
interface Repository<T> {
create(item: T): Promise<T>;
findById(id: string): Promise<T | null>;
findAll(): Promise<T[]>;
update(id: string, item: Partial<T>): Promise<T>;
delete(id: string): Promise<void>;
}
class UserRepository implements Repository<User> {
async create(user: User): Promise<User> {
// Implementation
return user;
}
async findById(id: string): Promise<User | null> {
// Implementation
return null;
}
// ... other methods
}
7. Advanced Types
Utility Types
interface User {
id: number;
name: string;
email: string;
password: string;
isActive: boolean;
}
// Partial - all properties optional
type PartialUser = Partial<User>;
// equivalent to:
// {
// id?: number;
// name?: string;
// email?: string;
// password?: string;
// isActive?: boolean;
// }
// Required - all properties required
type RequiredUser = Required<PartialUser>;
// Pick - select specific properties
type PublicUser = Pick<User, 'id' | 'name' | 'email'>;
// Omit - exclude specific properties
type UserWithoutPassword = Omit<User, 'password'>;
// Record - create type with specific keys and values
type UserRoles = Record<'admin' | 'user' | 'guest', string[]>;
const roles: UserRoles = {
admin: ['read', 'write', 'delete'],
user: ['read', 'write'],
guest: ['read']
};
// Exclude/Extract with union types
type Status = 'pending' | 'approved' | 'rejected';
type ActiveStatus = Exclude<Status, 'rejected'>; // 'pending' | 'approved'
type NonActiveStatus = Extract<Status, 'rejected'>; // 'rejected'
// ReturnType - extract return type from function
function getUser(): User {
return {} as User;
}
type UserReturnType = ReturnType<typeof getUser>; // User
// Parameters - extract parameter types
type GetUserParams = Parameters<typeof getUser>; // []
Mapped Types
// Create read-only version of type
type ReadOnly<T> = {
readonly [P in keyof T]: T[P];
};
// Create optional version of type
type Optional<T> = {
[P in keyof T]?: T[P];
};
// Create nullable version of type
type Nullable<T> = {
[P in keyof T]: T[P] | null;
};
type NullableUser = Nullable<User>;
// Conditional types
type IsString<T> = T extends string ? true : false;
type Test1 = IsString<string>; // true
type Test2 = IsString<number>; // false
// More complex conditional type
type ApiResponse<T> = T extends string
? { message: T }
: T extends number
? { code: T }
: { data: T };
type StringResponse = ApiResponse<string>; // { message: string }
type NumberResponse = ApiResponse<number>; // { code: number }
type ObjectResponse = ApiResponse<User>; // { data: User }
8. Decorators
Class Decorators
// Class decorator
function Component(target: any) {
target.prototype.render = function() {
console.log("Rendering component...");
};
}
@Component
class MyComponent {
name = "MyComponent";
}
const component = new MyComponent();
(component as any).render(); // "Rendering component..."
// Decorator factory
function Entity(tableName: string) {
return function(target: any) {
target.prototype.tableName = tableName;
};
}
@Entity("users")
class User {
id: number;
name: string;
}
Method Decorators
// Method decorator
function Log(target: any, propertyName: string, descriptor: PropertyDescriptor) {
const method = descriptor.value;
descriptor.value = function (...args: any[]) {
console.log(`Calling ${propertyName} with args:`, args);
const result = method.apply(this, args);
console.log(`${propertyName} returned:`, result);
return result;
};
}
class Calculator {
@Log
add(a: number, b: number): number {
return a + b;
}
@Log
multiply(a: number, b: number): number {
return a * b;
}
}
// Property decorator
function MinLength(length: number) {
return function (target: any, propertyName: string) {
let value: string;
const getter = () => value;
const setter = (newValue: string) => {
if (newValue.length < length) {
throw new Error(`${propertyName} must be at least ${length} characters`);
}
value = newValue;
};
Object.defineProperty(target, propertyName, {
get: getter,
set: setter,
enumerable: true,
configurable: true
});
};
}
class User {
@MinLength(3)
public name: string;
}
9. Modules ve Namespaces
ES6 Modules
// user.ts
export interface User {
id: number;
name: string;
email: string;
}
export class UserService {
private users: User[] = [];
addUser(user: User): void {
this.users.push(user);
}
getUsers(): User[] {
return this.users;
}
}
export const DEFAULT_USER: User = {
id: 0,
name: "Anonymous",
email: "anonymous@example.com"
};
// Default export
export default class ApiClient {
baseUrl: string;
constructor(baseUrl: string) {
this.baseUrl = baseUrl;
}
}
// main.ts
import ApiClient, { User, UserService, DEFAULT_USER } from './user';
import * as UserModule from './user';
const client = new ApiClient('https://api.example.com');
const userService = new UserModule.UserService();
Namespaces
// shapes.ts
namespace Shapes {
export interface Point {
x: number;
y: number;
}
export class Circle {
constructor(public center: Point, public radius: number) {}
area(): number {
return Math.PI * this.radius ** 2;
}
}
export class Rectangle {
constructor(
public topLeft: Point,
public width: number,
public height: number
) {}
area(): number {
return this.width * this.height;
}
}
}
// Usage
const circle = new Shapes.Circle({ x: 0, y: 0 }, 10);
const rectangle = new Shapes.Rectangle({ x: 0, y: 0 }, 20, 30);
10. Best Practices
Type Safety Best Practices
// ✅ Use strict TypeScript configuration
// tsconfig.json
{
"compilerOptions": {
"strict": true,
"noImplicitAny": true,
"noImplicitReturns": true,
"noUnusedLocals": true
}
}
// ✅ Use type assertions carefully
// Bad
const element = document.getElementById('my-element') as HTMLInputElement;
// Good
const element = document.getElementById('my-element');
if (element instanceof HTMLInputElement) {
element.value = 'typed value';
}
// ✅ Use type guards for runtime checks
function isString(value: unknown): value is string {
return typeof value === 'string';
}
// ✅ Prefer interfaces for object shapes
interface UserConfig {
apiUrl: string;
timeout: number;
retries?: number;
}
// ✅ Use enums for related constants
enum HttpStatus {
OK = 200,
NotFound = 404,
InternalServerError = 500
}
// ✅ Use readonly for immutable data
interface ReadonlyConfig {
readonly apiUrl: string;
readonly features: readonly string[];
}
Performance ve Organization
// ✅ Use index signatures efficiently
interface Config {
[key: string]: string | number | boolean;
// Known properties for better type checking
apiUrl: string;
timeout: number;
}
// ✅ Organize types in separate files
// types/user.ts
export interface User {
id: string;
name: string;
email: string;
}
export type UserRole = 'admin' | 'user' | 'guest';
// types/api.ts
export interface ApiResponse<T> {
data: T;
status: number;
message?: string;
}
// types/index.ts
export * from './user';
export * from './api';
// ✅ Use barrel exports
// index.ts
export { UserService } from './services/UserService';
export { ApiClient } from './clients/ApiClient';
export * from './types';
11. Real-World Examples
Express.js with TypeScript
// types.ts
export interface CreateUserRequest {
name: string;
email: string;
password: string;
}
export interface User {
id: string;
name: string;
email: string;
createdAt: Date;
}
// userController.ts
import { Request, Response } from 'express';
import { CreateUserRequest, User } from './types';
export class UserController {
async createUser(
req: Request<{}, User, CreateUserRequest>,
res: Response<User | { error: string }>
): Promise<void> {
try {
const { name, email, password } = req.body;
const user: User = {
id: generateId(),
name,
email,
createdAt: new Date()
};
// Save user logic...
res.status(201).json(user);
} catch (error) {
res.status(400).json({ error: error.message });
}
}
}
// app.ts
import express from 'express';
import { UserController } from './userController';
const app = express();
const userController = new UserController();
app.use(express.json());
app.post('/users', userController.createUser.bind(userController));
export default app;
React with TypeScript
// components/UserCard.tsx
import React from 'react';
interface UserCardProps {
user: {
id: string;
name: string;
email: string;
avatar?: string;
};
onEdit: (userId: string) => void;
onDelete: (userId: string) => void;
className?: string;
}
export const UserCard: React.FC<UserCardProps> = ({
user,
onEdit,
onDelete,
className
}) => {
const handleEdit = () => onEdit(user.id);
const handleDelete = () => onDelete(user.id);
return (
<div className={`user-card ${className || ''}`}>
{user.avatar && (
<img src={user.avatar} alt={`${user.name}'s avatar`} />
)}
<h3>{user.name}</h3>
<p>{user.email}</p>
<div className="actions">
<button onClick={handleEdit}>Edit</button>
<button onClick={handleDelete}>Delete</button>
</div>
</div>
);
};
// hooks/useUsers.ts
import { useState, useEffect } from 'react';
interface User {
id: string;
name: string;
email: string;
}
export const useUsers = () => {
const [users, setUsers] = useState<User[]>([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
fetchUsers();
}, []);
const fetchUsers = async (): Promise<void> => {
try {
setLoading(true);
const response = await fetch('/api/users');
if (!response.ok) {
throw new Error('Failed to fetch users');
}
const userData: User[] = await response.json();
setUsers(userData);
} catch (err) {
setError(err instanceof Error ? err.message : 'Unknown error');
} finally {
setLoading(false);
}
};
return { users, loading, error, refetch: fetchUsers };
};
Sonuç
TypeScript modern web development'ın vazgeçilmez bir parçası haline gelmiştir. Bu rehberde öğrendiklerinizi özetlersek:
Ana Faydalar:
- Compile-time hata yakalama ile daha güvenli kod
- Mükemmel IDE desteği ve IntelliSense
- Büyük projelerde maintainability
- Team collaboration'da consistency
- Refactoring güvenliği
Best Practices:
- Strict mode kullanın
- Type guards ile runtime checks yapın
- Generic'leri type safety için kullanın
- Utility types'lar ile kod tekrarını azaltın
- Types'ları organize edin ve export edin
TypeScript öğrenmek için en iyi yol mevcut JavaScript projelerinizi kademeli olarak TypeScript'e migrate etmektir. Başlangıçta basic type annotations kullanarak başlayın ve zamanla advanced features'ları projenize entegre edin.