strictmodunun hangi denetimleri açtığını açıklamakas constile değerlerden kesin literal tipler elde etmeksatisfies'ı tip belirtimi veasile karşılaştırmak@typespaketlerini kullanmak ve basit bir.d.tsdosyası yazmak
Artık tipleri, fonksiyonları, arayüzleri, birleşimleri, sınıfları ve jenerikleri biliyorsun. Bu son ders, profesyonellerin her gün kullandığı dört araç hakkında: hataların çoğunu yakalayan katı mod, değerlerden kesin tip almak için as const, tipi genişletmeden denetleyen satisfies ve tipsiz JavaScript kütüphanelerini TypeScript'e tanıtan bildirim dosyaları.
Katı mod
tsconfig.json içindeki "strict": true tek bir seçenek değil, bütün bir denetim kümesidir. Onsuz TypeScript pek çok şeye göz yumar ve JavaScript'in en sık görülen hataları gözden kaçar. Kümenin en önemli üyeleri şunlardır:
strictNullChecks:nullveundefinedayrı tiplerdir; “undefinedolabilecek” bir değer kontrol edilmeden kullanılamaz.noImplicitAny: tipi belirlenemeyen bir parametre sessizceanyolmaz, hata verilir.strictPropertyInitialization: her sınıf alanı tanımında ya da yapıcıda bir değer almalıdır.useUnknownInCatchVariables:catch (error)içindeerror'ın tipianydeğilunknownolur.
const users = [
{ name: 'Aysel', age: 15 },
{ name: 'Murad', age: 17 },
];
const found = users.find((u) => u.name === 'Leyla');
console.log(found.age);users.ts:7:13 - error TS18048: 'found' is possibly 'undefined'.
7 console.log(found.age);
~~~~~find hiçbir şey bulamayabilir. Katı mod olmasaydı kod hatasız derlenir, çalışma anında ise TypeError: Cannot read properties of undefined (reading 'age') ile çökerdi.const users = [
{ name: 'Aysel', age: 15 },
{ name: 'Murad', age: 17 },
];
const found = users.find((u) => u.name === 'Leyla');
console.log(found?.age ?? 'not found');not found
?. ve ?? ile ele almak.as const
as const, bir değerin “dondurulmuş” kabul edilmesini ister: string'ler string yerine kesin literal tipler olur, dizi salt okunur bir demete, nesnenin özellikleri de readonly özelliklere dönüşür. Bu, roller, durumlar ve ayarlar gibi sabit listeler için idealdir ve daha önce konuştuğumuz enum'un yerini rahatça alır:
const ROLES = ['admin', 'editor', 'viewer'] as const;
type Role = (typeof ROLES)[number];
function canEdit(role: Role): boolean {
return role !== 'viewer';
}
const Status = { Active: 'active', Blocked: 'blocked' } as const;
type Status = (typeof Status)[keyof typeof Status];
const s: Status = Status.Blocked;
console.log(ROLES.join(', '), canEdit('editor'), s);admin, editor, viewer true blocked
Role tipi 'admin' | 'editor' | 'viewer', Status tipi ise 'active' | 'blocked''tır. Liste programda da vardır; yazdırılabilir ve döngüyle gezilebilir.const ROLES = ['admin', 'editor', 'viewer'] as const;
ROLES.push('guest');roles.ts:2:7 - error TS2339: Property 'push' does not exist on type 'readonly ["admin", "editor", "viewer"]'.
2 ROLES.push('guest');
~~~~as const diziyi salt okunur bir demete dönüştürür; bu yüzden push metodu yoktur.satisfies operatörü
Tip belirtimi (const x: T = ...) değeri denetler ama değişkenin tipini T'ye genişletir ve değerle ilgili kesin bilgi kaybolur. satisfies T ise yalnızca denetler: değer T'ye uymalıdır, değişken ise TypeScript'in çıkardığı kesin tipi korur. Bir renk paletinde bazı renkler string, bazıları üç sayıdan oluşan demetlerdir:
type ColorName = 'red' | 'green' | 'blue';
type Color = string | [number, number, number];
const palette = {
red: [255, 0, 0],
green: '#00ff00',
blue: [0, 0, 255],
} satisfies Record<ColorName, Color>;
console.log(palette.green.toUpperCase());
console.log(palette.red[0]);#00FF00 255
green'in bir string, red'in ise bir demet olduğunu bilir, çünkü satisfies kesin tipi korur.type ColorName = 'red' | 'green' | 'blue';
type Color = string | [number, number, number];
const annotated: Record<ColorName, Color> = {
red: [255, 0, 0],
green: '#00ff00',
blue: [0, 0, 255],
};
annotated.green.toUpperCase();
const checked = {
red: [255, 0, 0],
green: '#00ff00',
bleu: [0, 0, 255],
} satisfies Record<ColorName, Color>;palette.ts:9:17 - error TS2339: Property 'toUpperCase' does not exist on type 'Color'.
Property 'toUpperCase' does not exist on type '[number, number, number]'.
9 annotated.green.toUpperCase();
~~~~~~~~~~~
palette.ts:14:3 - error TS2353: Object literal may only specify known properties, and 'bleu' does not exist in type 'Record<ColorName, Color>'.
14 bleu: [0, 0, 255],
~~~~green yalnızca bir Color'dır. İkinci hata: satisfies yine de denetler ve bleu yazım hatasını yakalar.| Yazım | Denetler mi? | Değişkenin tipi |
|---|---|---|
const x: T = value | evet | T (genişler) |
const x = value satisfies T | evet | değerin kesin tipi |
const x = value as T | neredeyse hiç | T |
Bildirim dosyaları ve üçüncü taraf kütüphaneler
Yalnızca tip içeren, çalıştırılabilir hiçbir kodu olmayan bir dosya. JavaScript kodunun TypeScript için yazılmış “kullanım kılavuzudur”: hangi fonksiyonlar var, hangi parametreleri alıyor ve ne döndürüyor.
npm paketlerinin tipleri üç yoldan biriyle gelir. Pek çok modern paket kendi .d.ts dosyalarıyla birlikte gelir ve yapılacak bir şey yoktur. Diğerleri için topluluk tipleri ayrı @types/... paketlerinde yayımlar. İkisi de yoksa bildirim dosyasını kendin yazarsın.
npm install lodash
npm install --save-dev @types/lodashdependencies'e, tipleri ise yalnızca geliştirme sırasında gerektiği için devDependencies'e girer.Diyelim ki projede tipleri olmayan eski bir old-slug kütüphanesi var; metni bir URL için kısa bir ada dönüştürüyor. Onu içe aktardığında TypeScript, modülün tiplerini bulamadığını bildirir:
src/index.ts:1:21 - error TS7016: Could not find a declaration file for module 'old-slug'. '/home/leyla/blog/node_modules/old-slug/index.js' implicitly has an 'any' type.
Try `npm i --save-dev @types/old-slug` if it exists or add a new declaration (.d.ts) file containing `declare module 'old-slug';`
1 import slugify from 'old-slug';
~~~~~~~~~~@types/old-slug paketi yoksa types/old-slug.d.ts dosyasını oluşturur ve modülü declare module ile tanımlarız. declare sözcüğü, bu kodun başka bir yerde zaten var olduğunu, bizim yalnızca tipini bildirdiğimizi söyler:
declare module 'old-slug' {
export default function slugify(text: string, separator?: string): string;
}types/old-slug.d.ts: gövdesiz bir fonksiyon imzası, yalnızca tipler.import slugify from 'old-slug';
const title = 'Hello from Baku';
console.log(slugify(title));
console.log(slugify(title, '_'));hello-from-baku hello_from_baku
src/index.ts artık hatasız denetlenir, editör parametreleri önerir; slugify(42) gibi bir çağrı ise TS2345 hatası verir.Kendi kütüphaneni yayımlarken .d.ts dosyalarını elle yazman gerekmez. tsconfig.json içindeki "declaration": true seçeneğiyle tsc, her .js dosyasının yanına onun .d.ts dosyasını da üretir:
export interface User {
name: string;
age: number;
}
export declare function greet(user: User): string;greet.ts'ten üretilen greet.d.ts: arayüz ve fonksiyonun imzası kaldı, gövde ise greet.js içinde.Önemli noktalar
strict: true,strictNullChecks,noImplicitAnyve diğer denetimleri açar; her zaman açık tut.asve!hata mesajını gizler ama sorunu çözmez.as constbir değeri kesin literal ve salt okunur tiplere dönüştürür;(typeof LIST)[number]listeden bir birleşim tipi çıkarır.satisfies TdeğeriT'ye göre denetler ama değişkenin kesin tipini korur..d.tsdosyaları yalnızca tip içerir; tipler paketin kendisinden,@types/...paketlerinden ya dadeclare moduleile yazdığın bir dosyadan gelir.
Kendini test et
10 soru. Her doğru cevap XP kazandırır.
tsconfig.json içindeki "strict": true ne yapar?