Karmaşık web uygulamaları oluşturuyorsanız TypeScript muhtemelen programlama diliniz olacaktır. TypeScript, güçlü tür sistemi ve statik analiz yetenekleri nedeniyle çok sevilir ve bu da onu kodunuzun sağlam ve hatasız olmasını sağlamak için güçlü bir araç haline getirir.
Ayrıca kod düzenleyicilerle entegrasyon yoluyla geliştirme sürecini hızlandırarak geliştiricilerin kodda daha verimli bir şekilde gezinmesine, daha doğru ipuçları ve otomatik tamamlama almasına olanak tanır ve büyük miktarlarda kodun güvenli bir şekilde yeniden düzenlenmesine olanak tanır.
Derleyici, TypeScript'in kalbidir ve tür doğruluğunu kontrol etmekten ve TypeScript kodunu JavaScript'e dönüştürmekten sorumludur. Ancak TypeScript'in gücünden tam olarak yararlanmak için Derleyiciyi doğru şekilde yapılandırmak önemlidir.
Her TypeScript projesinde Derleyicinin tüm yapılandırma seçeneklerini içeren bir veya daha fazla tsconfig.json
dosyası bulunur.
Tsconfig'i yapılandırmak, TypeScript projelerinizde optimum tür güvenliği ve geliştirici deneyimi elde etmek için çok önemli bir adımdır. İlgili tüm önemli faktörleri dikkatle değerlendirmek için zaman ayırarak geliştirme sürecini hızlandırabilir ve kodunuzun sağlam ve hatasız olduğundan emin olabilirsiniz.
Tsconfig'teki varsayılan yapılandırma, geliştiricilerin TypeScript'in avantajlarının çoğunu kaçırmasına neden olabilir. Bunun nedeni, pek çok güçlü tür denetimi özelliğini etkinleştirmemesidir. "Varsayılan" yapılandırmayla, hiçbir tür denetimi derleyici seçeneğinin ayarlanmadığı bir yapılandırmayı kastediyorum.
Örneğin:
{ "compilerOptions": { "target": "esnext", "module": "esnext", "esModuleInterop": true, "forceConsistentCasingInFileNames": true, "skipLibCheck": true, }, "include": ["src"] }
Birkaç temel yapılandırma seçeneğinin bulunmaması, iki temel nedenden dolayı kod kalitesinin düşmesine neden olabilir. İlk olarak, TypeScript'in derleyicisi çeşitli durumlarda null
ve undefined
türleri hatalı şekilde işleyebilir.
İkinci olarak, any
tür kod tabanınızda kontrolsüz bir şekilde görünebilir ve bu da bu türün devre dışı bırakılmasına yol açabilir.
Neyse ki, yapılandırmadaki birkaç seçeneği değiştirerek bu sorunları düzeltmek kolaydır.
{ "compilerOptions": { "strict": true } }
Katı mod, çok çeşitli tür denetimi davranışlarını etkinleştirerek programın doğruluğu konusunda daha güçlü garantiler sağlayan önemli bir yapılandırma seçeneğidir.
Tsconfig dosyasında katı modun etkinleştirilmesi, maksimum tür güvenliğine ve daha iyi bir geliştirici deneyimine ulaşma yolunda önemli bir adımdır.
Tsconfig'i yapılandırmak biraz daha fazla çaba gerektirir, ancak projenizin kalitesini artırmada uzun bir yol kat edebilir.
strict
derleyici seçeneği, diğerlerinin yanı sıra noImplicitAny
, strictNullChecks
, strictFunctionTypes
dahil tüm katı mod ailesi seçeneklerini etkinleştirir.
Bu seçenekler ayrı ayrı da yapılandırılabilir ancak hiçbirinin kapatılması önerilmez. Nedenini görmek için örneklere bakalım.
{ "compilerOptions": { "noImplicitAny": true } }
any
tür, statik tür sisteminde tehlikeli bir boşluktur ve bunun kullanılması tüm tür denetimi kurallarını devre dışı bırakır. Sonuç olarak TypeScript'in tüm avantajları kaybolur: hatalar gözden kaçırılır, kod düzenleyici ipuçları düzgün çalışmayı durdurur vb.
any
kullanılması yalnızca aşırı durumlarda veya prototip oluşturma ihtiyaçları için uygundur. En iyi çabalarımıza rağmen, any
tür bazen dolaylı olarak kod tabanına gizlice girebilir.
Varsayılan olarak derleyici, any
hatanın kod tabanında görünmesi karşılığında bizi birçok hatayı affeder. Spesifik olarak TypeScript, tür otomatik olarak çıkarılamasa bile değişkenlerin türünü belirtmememize olanak tanır.
Sorun, yanlışlıkla bir değişkenin türünü (örneğin bir işlev argümanına) belirtmeyi unutabilmemizdir. TypeScript, bir hata göstermek yerine değişkenin türünü otomatik olarak any
şekilde çıkaracaktır.
function parse(str) { // ^? any return str.split(''); } // TypeError: str.split is not a function const res1 = parse(42); const res2 = parse('hello'); // ^? any
noImplicitAny
derleyici seçeneğinin etkinleştirilmesi, derleyicinin değişken türünün otomatik olarak any
olarak çıkarıldığı tüm yerleri vurgulamasına neden olur. Örneğimizde TypeScript bizden fonksiyon argümanının tipini belirtmemizi isteyecektir.
function parse(str) { // ^ Error: Parameter 'str' implicitly has an 'any' type. return str.split(''); }
Türü belirttiğimizde TypeScript, bir sayıyı string parametresine geçirme hatasını hızlı bir şekilde yakalayacaktır. res2
değişkeninde saklanan fonksiyonun dönüş değeri de doğru türde olacaktır.
function parse(str: string) { return str.split(''); } const res1 = parse(42); // ^ Error: Argument of type 'number' is not // assignable to parameter of type 'string' const res2 = parse('hello'); // ^? string[]
{ "compilerOptions": { "useUnknownInCatchVariables": true } }
useUnknownInCatchVariables
yapılandırılması, try-catch bloklarındaki istisnaların güvenli bir şekilde işlenmesine olanak tanır. Varsayılan olarak TypeScript, catch bloğundaki hata türünün any
olduğunu varsayar ve bu da hatayla ilgili her şeyi yapmamıza olanak tanır.
Örneğin, yakalanan hatayı olduğu gibi Error
örneğini kabul eden bir günlük kaydı işlevine iletebiliriz.
function logError(err: Error) { // ... } try { return JSON.parse(userInput); } catch (err) { // ^? any logError(err); }
Ancak gerçekte hatanın türü hakkında hiçbir garanti yoktur ve gerçek türünü yalnızca hata oluştuğunda çalışma zamanında belirleyebiliriz. Günlük işlevi Error
olmayan bir şey alırsa, bu bir çalışma zamanı hatasıyla sonuçlanacaktır.
Bu nedenle, useUnknownInCatchVariables
seçeneği, hatayla ilgili any
bir şey yapmadan önce hatanın türünü kontrol etmemizi bize hatırlatmak için hatanın türünü herhangi bir durumdan unknown
değiştirir.
try { return JSON.parse(userInput); } catch (err) { // ^? unknown // Now we need to check the type of the value if (err instanceof Error) { logError(err); } else { logError(new Error('Unknown Error')); } }
Artık TypeScript, logError
işlevine aktarmadan önce err
değişkeninin türünü kontrol etmemizi isteyecek, böylece daha doğru ve daha güvenli kod elde edeceğiz. Ne yazık ki bu seçenek, promise.catch()
işlevlerinde veya geri çağırma işlevlerindeki yazım hatalarında yardımcı olmaz.
Ancak bir sonraki makalede bu gibi any
başa çıkmanın yollarını tartışacağız.
{ "compilerOptions": { "strictBindCallApply": true } }
Başka bir seçenek, any
işlev içi çağrının görünümünü call
ve apply
yoluyla düzeltir. Bu, ilk ikisine göre daha az görülen bir durumdur ancak yine de dikkate alınması önemlidir. Varsayılan olarak TypeScript bu tür yapılardaki türleri hiçbir şekilde denetlemez.
Örneğin, herhangi bir şeyi bir fonksiyona argüman olarak iletebiliriz ve sonunda her zaman any
tipini alırız.
function parse(value: string) { return parseInt(value, 10); } const n1 = parse.call(undefined, '10'); // ^? any const n2 = parse.call(undefined, false); // ^? any
strictBindCallApply
seçeneğinin etkinleştirilmesi TypeScript'i daha akıllı hale getirir, böylece dönüş türü doğru bir şekilde number
olarak anlaşılır. Ve yanlış türde bir argüman iletilmeye çalışıldığında TypeScript hatayı gösterecektir.
function parse(value: string) { return parseInt(value, 10); } const n1 = parse.call(undefined, '10'); // ^? number const n2 = parse.call(undefined, false); // ^ Argument of type 'boolean' is not // assignable to parameter of type 'string'.
{ "compilerOptions": { "noImplicitThis": true } }
Projenizde any
görünmesini engellemeye yardımcı olabilecek bir sonraki seçenek, işlev çağrılarında yürütme bağlamının işlenmesini düzeltir. JavaScript'in dinamik doğası, bir işlevin içindeki bağlamın türünü statik olarak belirlemeyi zorlaştırır.
TypeScript, varsayılan olarak bu gibi durumlarda bağlam için any
türünü kullanır ve herhangi bir uyarı sağlamaz.
class Person { private name: string; constructor(name: string) { this.name = name; } getName() { return function () { return this.name; // ^ 'this' implicitly has type 'any' because // it does not have a type annotation. }; } }
noImplicitThis
derleyici seçeneğinin etkinleştirilmesi, bir işlevin bağlam türünü açıkça belirtmemizi isteyecektir. Böylece yukarıdaki örnekte Person
sınıfının name
alanı yerine fonksiyon içeriğine erişme hatasını yakalayabiliriz.
{ "compilerOptions": { "strictNullChecks": true } }
strict
modda yer alan sonraki birkaç seçenek, kod tabanında any
türün görünmesine neden olmaz. Ancak TS derleyicisinin davranışını daha katı hale getirirler ve geliştirme sırasında daha fazla hatanın bulunmasına izin verirler.
Bu tür ilk seçenek TypeScript'te null
ve undefined
işlenmesini düzeltir. TypeScript, varsayılan olarak null
ve undefined
değerlerin herhangi bir tür için geçerli değerler olduğunu varsayar; bu da beklenmeyen çalışma zamanı hatalarına neden olabilir.
strictNullChecks
derleyici seçeneğinin etkinleştirilmesi, geliştiriciyi null
ve undefined
durumların oluşabileceği durumları açıkça ele almaya zorlar.
Örneğin aşağıdaki kodu göz önünde bulundurun:
const users = [ { name: 'Oby', age: 12 }, { name: 'Heera', age: 32 }, ]; const loggedInUser = users.find(u => u.name === 'Max'); // ^? { name: string; age: number; } console.log(loggedInUser.age); // ^ TypeError: Cannot read properties of undefined
Bu kod hatasız derlenecektir ancak sistemde “Max” isimli kullanıcı mevcut değilse ve users.find()
fonksiyonu undefined
döndürüyorsa çalışma zamanı hatası verebilir. Bunu önlemek için strictNullChecks
derleyici seçeneğini etkinleştirebiliriz.
Artık TypeScript bizi, users.find()
tarafından döndürülen null
veya undefined
olasılığını açıkça ele almaya zorlayacak.
const loggedInUser = users.find(u => u.name === 'Max'); // ^? { name: string; age: number; } | undefined if (loggedInUser) { console.log(loggedInUser.age); }
null
ve undefiined
olasılığını açıkça ele alarak çalışma zamanı hatalarını önleyebilir ve kodumuzun daha sağlam ve hatasız olmasını sağlayabiliriz.
{ "compilerOptions": { "strictFunctionTypes": true } }
strictFunctionTypes
etkinleştirilmesi TypeScript'in derleyicisini daha akıllı hale getirir. Sürüm 2.6'dan önce TypeScript, işlev bağımsız değişkenlerinin çelişkisini kontrol etmiyordu. İşlev yanlış türde bir bağımsız değişkenle çağrılırsa bu, çalışma zamanı hatalarına yol açacaktır.
Örneğin, bir fonksiyon türü hem dizeleri hem de sayıları işleyebiliyor olsa bile, bu türe yalnızca dizeleri işleyebilen bir işlev atayabiliriz. Bu fonksiyona hala bir sayı iletebiliriz ancak çalışma zamanı hatası alırız.
function greet(x: string) { console.log("Hello, " + x.toLowerCase()); } type StringOrNumberFn = (y: string | number) => void; // Incorrect Assignment const func: StringOrNumberFn = greet; // TypeError: x.toLowerCase is not a function func(10);
Neyse ki, strictFunctionTypes
seçeneğinin etkinleştirilmesi bu davranışı düzeltir ve derleyici bu hataları derleme zamanında yakalayabilir ve bize işlevlerdeki tür uyumsuzluğuna ilişkin ayrıntılı bir mesaj gösterebilir.
const func: StringOrNumberFn = greet; // ^ Type '(x: string) => void' is not assignable to type 'StringOrNumberFn'. // Types of parameters 'x' and 'y' are incompatible. // Type 'string | number' is not assignable to type 'string'. // Type 'number' is not assignable to type 'string'.
{ "compilerOptions": { "strictPropertyInitialization": true } }
Son olarak, strictPropertyInitialization
seçeneği, değer olarak undefined
içermeyen türler için zorunlu sınıf özelliği başlatma işleminin kontrol edilmesini sağlar.
Örneğin, aşağıdaki kodda geliştirici email
özelliğini başlatmayı unuttu. Varsayılan olarak TypeScript bu hatayı algılamaz ve çalışma zamanında bir sorun ortaya çıkabilir.
class UserAccount { name: string; email: string; constructor(name: string) { this.name = name; // Forgot to assign a value to this.email } }
Ancak strictPropertyInitialization
seçeneği etkinleştirildiğinde TypeScript bu sorunu bizim için vurgulayacaktır.
email: string; // ^ Error: Property 'email' has no initializer and // is not definitely assigned in the constructor.
{ "compilerOptions": { "noUncheckedIndexedAccess": true } }
noUncheckedIndexedAccess
seçeneği strict
modun bir parçası değildir ancak projenizdeki kod kalitesini artırmanıza yardımcı olabilecek başka bir seçenektir. Dizin erişim ifadelerinin null
veya undefined
bir dönüş türüne sahip olup olmadığının kontrol edilmesini sağlar, bu da çalışma zamanı hatalarını önleyebilir.
Önbelleğe alınmış değerleri depolamak için bir nesnemizin olduğu aşağıdaki örneği düşünün. Daha sonra anahtarlardan birinin değerini alıyoruz. Elbette istenilen anahtara ait değerin gerçekten önbellekte mevcut olduğuna dair bir garantimiz yoktur.
Varsayılan olarak TypeScript, değerin var olduğunu ve string
türüne sahip olduğunu varsayar. Bu çalışma zamanı hatasına yol açabilir.
const cache: Record<string, string> = {}; const value = cache['key']; // ^? string console.log(value.toUpperCase()); // ^ TypeError: Cannot read properties of undefined
TypeScript'te noUncheckedIndexedAccess
seçeneğinin etkinleştirilmesi, undefined
dönüş türü için dizin erişim ifadelerinin kontrol edilmesini gerektirir; bu, çalışma zamanı hatalarından kaçınmamıza yardımcı olabilir. Bu aynı zamanda bir dizideki öğelere erişim için de geçerlidir.
const cache: Record<string, string> = {}; const value = cache['key']; // ^? string | undefined if (value) { console.log(value.toUpperCase()); }
Tartışılan seçeneklere bağlı olarak, optimum tür güvenliği için projenizin tsconfig.json
dosyasında strict
ve noUncheckedIndexedAccess
seçeneklerinin etkinleştirilmesi önemle tavsiye edilir.
{ "compilerOptions": { "strict": true, "noUncheckedIndexedAccess": true, } }
strict
seçeneğini zaten etkinleştirdiyseniz, strict: true
seçeneğinin kopyalanmasını önlemek için aşağıdaki seçenekleri kaldırmayı düşünebilirsiniz:
noImplicitAny
useUnknownInCatchVariables
strictBindCallApply
noImplicitThis
strictFunctionTypes
strictNullChecks
strictPropertyInitialization
Ayrıca tür sistemini zayıflatabilecek veya çalışma zamanı hatalarına neden olabilecek aşağıdaki seçeneklerin de kaldırılması önerilir:
keyofStringsOnly
noStrictGenericChecks
suppressImplicitAnyIndexErrors
suppressExcessPropertyErrors
Bu seçenekleri dikkatlice değerlendirip yapılandırarak TypeScript projelerinizde optimum tür güvenliğine ve daha iyi bir geliştirici deneyimine ulaşabilirsiniz.
TypeScript, derleyicisini ve yazım sistemini sürekli geliştirerek evriminde uzun bir yol kat etti. Ancak geriye dönük uyumluluğu sürdürmek için TypeScript yapılandırması, tür denetiminin kalitesini önemli ölçüde etkileyebilecek birçok seçenekle birlikte daha karmaşık hale geldi.
Bu seçenekleri dikkatlice değerlendirip yapılandırarak TypeScript projelerinizde optimum tür güvenliğine ve daha iyi bir geliştirici deneyimine ulaşabilirsiniz. Bir proje yapılandırmasında hangi seçeneklerin etkinleştirileceğini ve hangi seçeneklerin kaldırılacağını bilmek önemlidir.
Belirli seçenekleri devre dışı bırakmanın sonuçlarını anlamak, her biri için bilinçli kararlar vermenizi sağlayacaktır.
Kesin yazımın sonuçları olabileceğini akılda tutmak önemlidir. JavaScript'in dinamik doğasını etkili bir şekilde ele almak için, bir değişkenden sonra yalnızca "sayı" veya "dize" belirtmenin ötesinde TypeScript'i iyi anlamanız gerekir.
Ortaya çıkacak türle ilgili sorunları daha etkili bir şekilde çözmek için daha karmaşık yapılara ve TypeScript'in ilk kitaplık ve araç ekosistemine aşina olmanız gerekecektir.
Sonuç olarak kod yazmak biraz daha fazla çaba gerektirebilir ancak tecrübelerime dayanarak uzun vadeli projeler için bu çabaya değdiğini düşünüyorum.
Umarım bu makaleden yeni bir şeyler öğrenmişsinizdir. Bu bir serinin ilk bölümü. Bir sonraki makalede TypeScript'in standart kitaplığındaki türleri iyileştirerek daha iyi tür güvenliği ve kod kalitesine nasıl ulaşabileceğimizi tartışacağız. Bizi izlemeye devam edin ve okuduğunuz için teşekkürler!