Bu makalede7 bölüm
- Teknoloji
- TypeScript, ESLint (flat config), typescript-eslint, Prettier, openapi-typescript
- Konu
- Ekip için TypeScript kod tabanı standardı
- Seviye
- Orta
- Ön koşullar
- TypeScript ile proje geliştirme deneyimi
- npm/Node.js araç zinciri bilgisi
Aynı TypeScript kod tabanında beş geliştirici çalışıyorsa kaliteyi bireysel özen değil, ortak kurallar belirler. Derleyici ayarları, lint kuralları, klasör yapısı ve CI kontrolleri yazılı bir standarda bağlanmazsa herkes kendi alışkanlığını getirir; altı ay sonra kod tabanı birbirine benzemeyen parçalara bölünmüştür ve her değişiklik başka bir yeri kırar. Aşağıda kurumsal bir web uygulaması için uygulanabilir bir TypeScript standardı var: örnek tsconfig, ESLint yapılandırması, modül sınırı kuralları ve CI adımları.
Kod standardının amacı
Kod standardı stil tartışmasını bitirmek için değil, hatayı derleme anında yakalamak ve değişikliğin etkisini öngörülebilir kılmak için vardır. Her kural üç sorudan birine cevap vermeli: Çalışma zamanı hatasını önlüyor mu? Kodun okunmasını ve incelenmesini hızlandırıyor mu? Mimari sınırları koruyor mu? Bu üçüne de dokunmayan kuralı standarda koymayın; kod incelemesinde boş sürtünme üretir.
Standardın ayakta kalması için üç şart var: kurallar araçlarla otomatik uygulanmalı, aynı araçlar geliştirici makinesinde ve CI’da aynı sürümle çalışmalı, istisnalar gerekçesiyle kod içinde görünür olmalı. Yalnız wiki sayfasında yazan kural uygulanmıyordur.
tsconfig strict ayarları
"strict": true tek başına bir bayrak ailesini açar; aralarında noImplicitAny, strictNullChecks, strictFunctionTypes, strictBindCallApply, strictPropertyInitialization, noImplicitThis, alwaysStrict ve useUnknownInCatchVariables bulunur (yeni TypeScript sürümleri bu aileye bayrak ekleyebilir). Yeni projede bu tartışılmaz. Üzerine eklenmesini önerdiğimiz ve strict içinde olmayan bayraklar:
| Bayrak | Ne yakalar | Not |
|---|---|---|
noUncheckedIndexedAccess |
dizi[i] ve kayıt erişiminde undefined olasılığı |
En değerli ek bayrak; kodda kontrol ekletir |
exactOptionalPropertyTypes |
İsteğe bağlı alana açıkça undefined atanması |
API kalıplarında katı; ekip kararıyla |
noImplicitOverride |
Üst sınıf metodunu farkında olmadan ezmek | Sınıf kullanan kodda yararlı |
noFallthroughCasesInSwitch |
switch içinde unutulan break |
Düşük maliyet |
verbatimModuleSyntax |
Yalnız tip olan importların çalışma zamanına kalması | import type kullanımını zorunlu kılar |
{
"compilerOptions": {
"target": "ES2022",
"lib": ["ES2022", "DOM", "DOM.Iterable"],
"module": "ESNext",
"moduleResolution": "Bundler",
"jsx": "preserve",
"strict": true,
"noUncheckedIndexedAccess": true,
"noImplicitOverride": true,
"noFallthroughCasesInSwitch": true,
"verbatimModuleSyntax": true,
"isolatedModules": true,
"skipLibCheck": true,
"noEmit": true,
"paths": { "@/*": ["./src/*"] }
},
"include": ["src", "tests"],
"exclude": ["node_modules", "dist"]
}
Bu örnek, derleme çıktısını bir paketleyicinin (Next.js, Vite) ürettiği bir ön yüz projesi içindir; noEmit ile TypeScript yalnız tip denetleyicisi olarak çalışır. Doğrudan Node.js üzerinde çalışan bir arka uç serviste "module": "NodeNext" ve "moduleResolution": "NodeNext" daha doğrudur, çünkü Node’un ESM/CommonJS çözümleme kurallarını birebir modeller. paths takma adları yalnız derleyiciye yol tarifidir; import’ları yeniden yazmaz. Derleme temiz geçip üretimde “Cannot find module” hatası alan servislerin sık görülen sebebi budur. Saf Node.js’te aynı amaç için package.json içindeki "imports" alanı (#lib/* gibi alt yol importları) kullanılır.
Var olan bir projede bütün sıkı ayarları birden açarsanız yüzlerce hata çıkar ve değişiklik genellikle geri alınır. Pratik yol: bayrakları tek tek açın, hataları modül sahiplerine dağıtın, geçiş süresince // @ts-ignore yerine açıklamalı // @ts-expect-error kullanın. İkincisi, altındaki hata düzeldiğinde kendisi hata verir; gereksiz kalan istisna böylece görünür olur.
Lint ve biçimlendirme kuralları
Görev ayrımı net: Prettier biçimlendirir, ESLint doğruluk ve mimari kurallarını denetler. Girinti, tırnak türü veya satır uzunluğu kod incelemesinde hiç konuşulmamalı. ESLint 9 ile varsayılan olan “flat config” biçiminde, tip bilgisi kullanan typescript-eslint kurallarıyla örnek bir yapılandırma:
// eslint.config.mjs
import js from '@eslint/js';
import tseslint from 'typescript-eslint';
import prettier from 'eslint-config-prettier';
export default [
{ ignores: ['dist/**', '.next/**', 'src/generated/**'] },
js.configs.recommended,
...tseslint.configs.recommendedTypeChecked,
{
languageOptions: {
parserOptions: { projectService: true, tsconfigRootDir: import.meta.dirname },
},
rules: {
'@typescript-eslint/no-floating-promises': 'error',
'@typescript-eslint/no-misused-promises': 'error',
'@typescript-eslint/consistent-type-imports': 'error',
'@typescript-eslint/switch-exhaustiveness-check': 'error',
'@typescript-eslint/no-explicit-any': 'error',
'@typescript-eslint/ban-ts-comment': ['error', { 'ts-expect-error': 'allow-with-description' }],
},
},
{ files: ['**/*.{js,mjs,cjs}'], ...tseslint.configs.disableTypeChecked },
prettier,
];
Kural seçiminin gerekçeleri:
no-floating-promisesveno-misused-promises: BeklenmeyenPromise, hatanın sessizce yutulduğu ve kaydın yarım kaldığı arızaların en yaygın kaynağıdır. Log’da iz yoktur, veri eksiktir. Bu kurallar tip bilgisi ister;recommendedTypeCheckedsetinin seçilme sebebi bu.switch-exhaustiveness-check: Bir birleşim tipine (ör. sipariş durumu) yeni değer eklendiğinde, ele alınmayan bütünswitchblokları derlemede işaretlenir.no-explicit-any: Dış kaynaktan gelen veriunknownolarak alınır ve şema doğrulamasıyla daraltılır.
disableTypeChecked satırı, eslint.config.mjs gibi tsconfig kapsamı dışındaki JavaScript dosyalarında tip bilgisi gerektiren kuralları kapatır; yoksa bu dosyalar “proje hizmetinde bulunamadı” hatası verir. Tip bilgili lint büyük projelerde yavaşlar. Geliştirici makinesinde editör entegrasyonu ve değişen dosyalar için ön işleme (pre-commit) kancası yeterli; tam taramayı CI’ya bırakın.
Klasör yapısı ve modül sınırları
Kod tabanı büyüdükçe asıl sorun tipler değil, bağımlılık yönü olur: arayüz bileşeni doğrudan veri tabanı istemcisini import eder, bir özellik başka bir özelliğin iç dosyasına uzanır. Bir yeri değiştirirsiniz, ilgisiz bir ekran bozulur. Özellik (feature) odaklı bir yapı ve açık sınırlar bunu önler:
src/
app/ // rotalar ve sayfa kompozisyonu
features/
siparis/
index.ts // özelliğin dışa açık API’si
components/
server/ // veri erişimi, iş kuralları
schema.ts
stok/
index.ts
...
shared/
ui/ // tasarım sistemi bileşenleri
lib/ // tarih, para birimi, yardımcılar
generated/ // API şemasından üretilen tipler (elle düzenlenmez)
Kurallar: shared hiçbir features modülünü import edemez; bir özellik başka bir özelliğe yalnız index.ts üzerinden erişir; server klasörleri istemci bileşenlerinden import edilemez. İlk adım için ESLint’in yerleşik no-restricted-imports kuralı yeterli:
{
files: ['src/**/*.{ts,tsx}'],
rules: {
'no-restricted-imports': ['error', {
patterns: [{
group: ['@/features/*/*', '!@/features/*/index'],
message: 'Başka bir özelliğe yalnız index.ts üzerinden erişin.',
}],
}],
},
},
Bu desen bir özelliğin kendi içindeki dosyalarını da yakalar; bu yüzden özellik içi importlar göreli yolla (./components/...) yazılır, takma ad yalnız özellikler arası erişimde kullanılır. Katman matrisi daha karmaşıksa eslint-plugin-boundaries veya dependency-cruiser gibi araçlar bağımlılık kurallarını tanımlayıp CI’da doğrulamanızı sağlar. Next.js web uygulamalarında sunucu modüllerinin istemci paketine sızmasına karşı ek bir kilit olarak server-only paketi kullanılır; bu sınırı Next.js App Router’da Server ve Client Component ayrımı yazısında ayrıntılı anlattık.
API tiplerini paylaşmak ve üretmek
Ön yüzle arka uç arasındaki tipleri elle kopyalamak, kontratın sessizce kaymasının en kısa yoludur. Arka uçta alan adı değişir, ön yüz derlenir, hata üretimde patlar. Kural: API tipleri tek kaynaktan üretilir, elle yazılmaz. Kaynak, seçilen API stiline göre değişir:
| API stili | Tek kaynak | Üretim aracı örneği |
|---|---|---|
| REST | OpenAPI belgesi | openapi-typescript |
| GraphQL | Şema + istemci sorguları | GraphQL Code Generator |
| Aynı depo, TypeScript arka uç | Paylaşılan şema paketi | Zod şemasından z.infer |
npx openapi-typescript ./openapi.yaml -o ./src/generated/api.d.ts
Üretilen dosyalar src/generated altında tutulur, lint dışı bırakılır ve CI’da yeniden üretilip farkın sıfır olduğu kontrol edilir. Biri şemayı değiştirip tipleri güncellemeyi unutursa derleme kırılır. Kırılması gereken yer de orasıdır. Ancak derleme zamanı tipleri çalışma zamanı garantisi değildir: ağdan gelen veri sınırda yine bir şema kütüphanesiyle doğrulanmalı. Formlar için ayrıntı istemci ve sunucuda tek şemayla form doğrulama yazısında, API stilinin seçimi REST mi GraphQL mi karşılaştırmasında.
CI’da tip ve lint kontrolü
Standardı fiilen uygulayan yer CI’dır. Her birleştirme isteğinde şu adımlar ayrı ayrı çalışmalı; biri başarısız olursa birleştirme engellenmeli:
# package.json betikleri
"typecheck": "tsc --noEmit",
"lint": "eslint . --max-warnings=0",
"format:check": "prettier --check .",
"api:generate": "openapi-typescript ./openapi.yaml -o ./src/generated/api.d.ts"
# CI adımları
npm ci
npm run api:generate && git diff --exit-code src/generated
npm run typecheck
npm run lint
npm run format:check
--max-warnings=0 bilinçli bir seçim. Uyarı birikmesine izin veren bir lint adımı birkaç ay içinde kimsenin okumadığı yüzlerce satırlık bir çıktıya dönüşür. Okunmayan uyarı, uyarı değildir. Bir kural uyarı düzeyinde kalacaksa ya hataya yükseltin ya kapatın. Proje referansları (project references) kullanan monorepolarda tsc --noEmit yerine tsc -b ile artımlı derleme yapılır. Bu adımları bir iş hattına yerleştirmeyi GitHub Actions ile web projesi CI/CD hattı yazısı anlatıyor.
Standart belgesine girmesi gereken kontrol listesi:
- Node.js, TypeScript, ESLint ve Prettier sürümleri kilit dosyasıyla sabit; CI aynı sürümleri kullanıyor.
strictvenoUncheckedIndexedAccessaçık; istisnalar açıklamalı@ts-expect-errorile.- Tip bilgili lint kuralları ve
--max-warnings=0etkin. - Özellikler arası erişim yalnız
index.tsüzerinden; kural lint ile denetleniyor. - API tipleri üretiliyor, elle düzenlenmiyor; CI’da fark kontrolü var.
- Editör ayarları (
.editorconfig, önerilen eklentiler) depoda tanımlı.
Kurumsal bir uygulamanın kod tabanını sıfırdan kurarken veya devraldığınız bir projeyi bu standarda taşırken yapılandırmayı ve geçiş planını özel web uygulaması geliştirme hizmetimiz kapsamında birlikte hazırlayabiliriz. Derleyici bayraklarının tam başvurusu için TypeScript tsconfig başvurusu, lint kuralları için typescript-eslint dokümantasyonu esas alınmalı.
Sık sorulan sorular
Var olan büyük bir projede strict modu birden açmak mantıklı mı?
Çoğunlukla hayır. Önce noImplicitAny ve strictNullChecks gibi bayrakları tek tek açıp hataları modül modül kapatın, sonra strict: true’ya geçin. Geçiş süresince yeni dosyaların sıkı kurallara uyması lint ve kod incelemesiyle zorunlu tutulabilir.
tsconfig paths tanımı çalışma zamanında da geçerli olur mu?
Hayır. paths yalnız TypeScript derleyicisine modül çözümlemeyi anlatır, import yollarını yeniden yazmaz. Next.js ve Vite gibi araçlar bu tanımı okur; saf Node.js çalışma zamanında ise package.json içindeki imports alanı (# ile başlayan alt yol importları) veya derleme aracının takma ad desteği gerekir.
Prettier ve ESLint birlikte kullanıldığında kural çakışması nasıl önlenir?
Biçimlendirme tamamen Prettier’a bırakılır, eslint-config-prettier ile ESLint’in biçimlendirme kuralları kapatılır. ESLint yalnız doğruluk ve mimari kurallarla ilgilenir. CI’da prettier –check ve eslint ayrı adımlar olarak çalışır.
Lint kurallarında any tamamen yasaklanmalı mı?
Uygulama kodunda no-explicit-any kuralı hata düzeyinde tutulabilir; dış veri için any yerine unknown kullanılıp şema doğrulamasıyla daraltılması doğru yoldur. Eski kütüphane entegrasyonu gibi kaçınılmaz durumlarda gerekçeli bir eslint-disable yorumu ve kod incelemesi onayıyla istisna tanımlanır.
- Teknoloji
- TypeScript, ESLint (flat config), typescript-eslint, Prettier, openapi-typescript
- Konu
- Ekip için TypeScript kod tabanı standardı
- Seviye
- Orta
- Ön koşullar
- TypeScript ile proje geliştirme deneyimi
- npm/Node.js araç zinciri bilgisi
Modern Web Uygulamaları