TypeScript, JavaScript'e type safety ve gelişmiş geliştirici deneyimi ekleyen bir superset'tir. Bu kapsamlı rehberde, mevcut JavaScript projelerinizi nasıl TypeScript'e dönüştüreceğinizi, temel kavramları ve en iyi uygulamaları öğreneceksiniz.

TypeScript Neden Kullanmalıyız?

TypeScript'in JavaScript'e göre önemli avantajları:

  • Type Safety: Compile-time hata yakalama
  • Better IntelliSense: Gelişmiş kod tamamlama
  • Refactoring: Güvenli kod yeniden yapılandırma
  • Debugging: Daha kolay hata ayıklama
  • Team Collaboration: Takım çalışmasında tutarlılık
  • Modern Features: En yeni ES özelliklerine erken erişim

TypeScript Kurulumu

Global Kurulum

# TypeScript global kurulum
npm install -g typescript

# Versiyon kontrolü
tsc --version

# TypeScript Compiler
tsc --init  # tsconfig.json oluşturur

Proje Bazında Kurulum

# Development dependency olarak kurulum
npm install --save-dev typescript @types/node

# Package.json script'leri
{
  "scripts": {
    "build": "tsc",
    "dev": "tsc --watch",
    "start": "node dist/index.js"
  }
}

tsconfig.json Konfigürasyonu

{
  "compilerOptions": {
    "target": "ES2020",                    // Hedef JS versiyonu
    "module": "commonjs",                  // Modül sistemi
    "lib": ["ES2020", "DOM"],             // Kullanılacak kütüphaneler
    "outDir": "./dist",                   // Çıktı klasörü
    "rootDir": "./src",                   // Kaynak klasörü
    "strict": true,                       // Sıkı mod
    "esModuleInterop": true,              // ES6 modül uyumluluğu
    "skipLibCheck": true,                 // Lib dosyalarını geç
    "forceConsistentCasingInFileNames": true,
    
    // Gelişmiş özellikler
    "noUnusedLocals": true,               // Kullanılmayan değişkenler
    "noUnusedParameters": true,           // Kullanılmayan parametreler
    "noImplicitReturns": true,            // İmplicit return kontrolü
    "noFallthroughCasesInSwitch": true,   // Switch case kontrolü
    
    // Source map ve debugging
    "sourceMap": true,                    // Source map oluştur
    "declaration": true,                  // .d.ts dosyaları oluştur
    "declarationMap": true                // Declaration source map
  },
  "include": ["src/**/*"],                // Hangi dosyalar dahil
  "exclude": ["node_modules", "dist"]     // Hangi dosyalar hariç
}

Temel Type Annotations

Primitive Types

// JavaScript
function greet(name) {
  return "Hello, " + name;
}

// TypeScript
function greet(name: string): string {
  return "Hello, " + name;
}

// Temel tipler
let age: number = 25;
let isActive: boolean = true;
let username: string = "john_doe";
let data: any = { id: 1, name: "John" };

// Array tipleri
let numbers: number[] = [1, 2, 3];
let names: Array<string> = ["Alice", "Bob"];

// Tuple
let coordinate: [number, number] = [10, 20];

// Enum
enum Color {
  Red = "red",
  Green = "green",
  Blue = "blue"
}

Union ve Intersection Types

// Union types (OR)
type Status = "loading" | "success" | "error";
let currentStatus: Status = "loading";

function processId(id: string | number) {
  if (typeof id === "string") {
    return id.toUpperCase();
  }
  return id.toString();
}

// Intersection types (AND)
type User = {
  name: string;
  email: string;
};

type Admin = {
  permissions: string[];
};

type AdminUser = User & Admin;

const admin: AdminUser = {
  name: "John",
  email: "[email protected]",
  permissions: ["read", "write"]
};

Interface'ler ve Type Aliases

// Interface tanımlama
interface User {
  readonly id: number;        // Sadece okunabilir
  name: string;
  email: string;
  age?: number;               // Opsiyonel property
  preferences: {
    theme: "light" | "dark";
    notifications: boolean;
  };
}

// Interface extending
interface AdminUser extends User {
  permissions: string[];
  lastLogin: Date;
}

// Function interface
interface ApiResponse<T> {
  data: T;
  status: number;
  message: string;
}

// Type alias
type UserPreferences = {
  theme: "light" | "dark";
  language: string;
  timezone: string;
};

// Function types
type EventHandler = (event: Event) => void;
type DataProcessor<T> = (data: T) => T;

Generics

// Generic function
function identity<T>(arg: T): T {
  return arg;
}

const numberResult = identity<number>(42);
const stringResult = identity("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({ length: 10, value: 3 }); // OK

// Generic classes
class DataStorage<T> {
  private data: T[] = [];
  
  addItem(item: T): void {
    this.data.push(item);
  }
  
  getItems(): T[] {
    return [...this.data];
  }
  
  findItem(predicate: (item: T) => boolean): T | undefined {
    return this.data.find(predicate);
  }
}

const stringStorage = new DataStorage<string>();
const numberStorage = new DataStorage<number>();

Utility Types

interface User {
  id: number;
  name: string;
  email: string;
  password: string;
  isActive: boolean;
}

// Partial - Tüm property'leri opsiyonel yapar
type UserUpdate = Partial<User>;
const update: UserUpdate = { name: "New Name" };

// Pick - Belirli property'leri seçer
type UserPreview = Pick<User, "id" | "name" | "email">;

// Omit - Belirli property'leri çıkarır
type PublicUser = Omit<User, "password">;

// Required - Tüm property'leri zorunlu yapar
type RequiredUser = Required<Partial<User>>;

// Record - Key-value pairs oluşturur
type UserRoles = Record<string, string[]>;
const roles: UserRoles = {
  admin: ["read", "write", "delete"],
  user: ["read"]
};

// Readonly - Tüm property'leri readonly yapar
type ImmutableUser = Readonly<User>;

// ReturnType - Function'ın return tipini alır
function getUser(): User {
  return {} as User;
}
type UserReturnType = ReturnType<typeof getUser>; // User

Adım Adım Migration Stratejisi

1. Aşama: Hazırlık

# TypeScript ve type definitions kurulumu
npm install --save-dev typescript
npm install --save-dev @types/node @types/express

# Dosya uzantılarını değiştirme
# .js → .ts
# .jsx → .tsx (React için)

2. Aşama: Kademeli Geçiş

// tsconfig.json'da kademeli strict ayarlar
{
  "compilerOptions": {
    "allowJs": true,                    // JS dosyalarına izin ver
    "checkJs": false,                   // JS dosyalarını kontrol etme
    "noImplicitAny": false,            // Any tipini kabul et
    "strict": false                     // Strict mode kapalı
  }
}

// Zamanla bu ayarları aktif et:
// 1. noImplicitAny: true
// 2. strictNullChecks: true
// 3. strict: true

3. Aşama: Type Definitions Ekleme

// JavaScript
function calculateTotal(items) {
  return items.reduce((sum, item) => sum + item.price, 0);
}

// TypeScript migration
interface Item {
  id: string;
  name: string;
  price: number;
  quantity: number;
}

function calculateTotal(items: Item[]): number {
  return items.reduce((sum, item) => sum + item.price, 0);
}

Error Handling ve Type Guards

// Type guards
function isString(value: any): value is string {
  return typeof value === "string";
}

function isUser(obj: any): obj is User {
  return obj && 
         typeof obj.id === "number" &&
         typeof obj.name === "string" &&
         typeof obj.email === "string";
}

// Discriminated unions
interface LoadingState {
  kind: "loading";
}

interface SuccessState {
  kind: "success";
  data: any[];
}

interface ErrorState {
  kind: "error";
  message: string;
}

type AsyncState = LoadingState | SuccessState | ErrorState;

function handleState(state: AsyncState) {
  switch (state.kind) {
    case "loading":
      return "Loading...";
    case "success":
      return `Loaded ${state.data.length} items`;
    case "error":
      return `Error: ${state.message}`;
    default:
      // TypeScript bunu unreachable olarak işaretler
      const _exhaustiveCheck: never = state;
      return _exhaustiveCheck;
  }
}

React ile TypeScript

// Props interface
interface ButtonProps {
  children: React.ReactNode;
  onClick: () => void;
  variant?: "primary" | "secondary";
  disabled?: boolean;
}

const Button: React.FC<ButtonProps> = ({ 
  children, 
  onClick, 
  variant = "primary",
  disabled = false 
}) => {
  return (
    <button 
      className={`btn btn-${variant}`}
      onClick={onClick}
      disabled={disabled}
    >
      {children}
    </button>
  );
};

// Hooks with TypeScript
const [user, setUser] = useState<User | null>(null);
const [loading, setLoading] = useState<boolean>(false);

// Custom hook
function useApi<T>(url: string) {
  const [data, setData] = useState<T | null>(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<string | null>(null);

  useEffect(() => {
    fetch(url)
      .then(response => response.json())
      .then((data: T) => {
        setData(data);
        setLoading(false);
      })
      .catch(err => {
        setError(err.message);
        setLoading(false);
      });
  }, [url]);

  return { data, loading, error };
}

Common Patterns ve Best Practices

API Response Typing

// API response interfaces
interface ApiResponse<T> {
  success: boolean;
  data: T;
  message?: string;
  errors?: string[];
}

interface User {
  id: string;
  name: string;
  email: string;
}

interface PaginatedResponse<T> extends ApiResponse<T[]> {
  pagination: {
    page: number;
    limit: number;
    total: number;
    totalPages: number;
  };
}

// API functions
async function fetchUser(id: string): Promise<ApiResponse<User>> {
  const response = await fetch(`/api/users/${id}`);
  return response.json();
}

async function fetchUsers(): Promise<PaginatedResponse<User>> {
  const response = await fetch('/api/users');
  return response.json();
}

Environment Variables Typing

// env.d.ts
declare namespace NodeJS {
  interface ProcessEnv {
    NODE_ENV: 'development' | 'production' | 'test';
    DATABASE_URL: string;
    JWT_SECRET: string;
    PORT?: string;
  }
}

// config.ts
interface Config {
  port: number;
  database: {
    url: string;
  };
  jwt: {
    secret: string;
    expiresIn: string;
  };
}

const config: Config = {
  port: parseInt(process.env.PORT || '3000'),
  database: {
    url: process.env.DATABASE_URL
  },
  jwt: {
    secret: process.env.JWT_SECRET,
    expiresIn: '7d'
  }
};

Testing TypeScript

// Jest ile TypeScript testing
import { jest } from '@jest/globals';

// Mock typing
const mockUser: User = {
  id: '1',
  name: 'John Doe',
  email: '[email protected]',
  password: 'hashedpassword',
  isActive: true
};

// Function mocking
const mockFetch = jest.fn() as jest.MockedFunction<typeof fetch>;

beforeEach(() => {
  mockFetch.mockClear();
});

test('should fetch user data', async () => {
  mockFetch.mockResolvedValueOnce({
    ok: true,
    json: async () => ({ success: true, data: mockUser })
  } as Response);

  const result = await fetchUser('1');
  
  expect(result.success).toBe(true);
  expect(result.data).toEqual(mockUser);
});

Performance ve Bundle Size

  • Tree Shaking: Kullanılmayan kodları temizleme
  • Type-Only Imports: import type { User } from './types'
  • Lazy Loading: Dynamic imports ile code splitting
  • Build Optimization: Production build'de type checking atla

Migration Checklist

  • ✅ TypeScript ve @types/* paketleri kuruldu
  • ✅ tsconfig.json konfigüre edildi
  • ✅ Build script'leri güncellendi
  • ✅ .js dosyaları .ts'ye dönüştürüldü
  • ✅ Any tiplerini minimize et
  • ✅ Interface'ler ve type'lar tanımlandı
  • ✅ Strict mode aktif edildi
  • ✅ Test'ler TypeScript'e dönüştürüldü
  • ✅ CI/CD pipeline güncellendi

Sonuç

TypeScript'e geçiş, başlangıçta zaman alsa da uzun vadede kod kalitesi, developer experience ve team productivity açısından büyük kazanımlar sağlar. Kademeli migration stratejisi ile mevcut projelerinizi güvenle TypeScript'e dönüştürebilirsiniz.

Web Studio TR'de sunduğumuz Frontend ve Backend Geliştirme kurslarında TypeScript'in tüm özelliklerini uygulamalı projelerle öğrenebilir, modern yazılım geliştirme becerilerinizi geliştirebilirsiniz.