paint-brush
TypeScript'i Gerçekten "Kuvvetle Yazılmış" Hale Getirmekile@nodge
14,562 okumalar
14,562 okumalar

TypeScript'i Gerçekten "Kuvvetle Yazılmış" Hale Getirmek

ile Maksim Zemskov12m2023/09/10
Read on Terminal Reader
Read this story w/o Javascript

Çok uzun; Okumak

TypeScript, veri şeklinin önceden bilinmediği durumlar için "Herhangi biri" tipini sağlar. Ancak bu türün aşırı kullanımı tür güvenliği, kod kalitesi ve geliştirici deneyimiyle ilgili sorunlara yol açabilir. Bu makale "Herhangi biri" türüyle ilişkili riskleri araştırıyor, kod tabanına dahil edilmesinin olası kaynaklarını belirliyor ve bir proje boyunca kullanımını kontrol etmeye yönelik stratejiler sağlıyor.

People Mentioned

Mention Thumbnail
featured image - TypeScript'i Gerçekten "Kuvvetle Yazılmış" Hale Getirmek
Maksim Zemskov HackerNoon profile picture
0-item
1-item

TypeScript, JavaScript'in üzerine kurulu, güçlü bir şekilde yazılmış bir programlama dili olduğunu ve her ölçekte daha iyi araçlar sağladığını iddia ediyor. Bununla birlikte, TypeScript any türü içerir ve bu genellikle kod tabanına gizlice sızarak TypeScript'in birçok avantajının kaybolmasına neden olabilir.


Bu makalede TypeScript projelerinde any türün kontrolünü ele almanın yolları araştırılmaktadır. TypeScript'in gücünü açığa çıkarmaya, üstün tür güvenliği elde etmeye ve kod kalitesini artırmaya hazır olun.

TypeScript'te Herhangi Birini Kullanmanın Dezavantajları

TypeScript, geliştirici deneyimini ve üretkenliğini artırmak için bir dizi ek araç sağlar:


  • Hataların geliştirme aşamasında erken yakalanmasına yardımcı olur.
  • Kod editörleri ve IDE'ler için mükemmel otomatik tamamlama sunar.
  • Harika kod gezinme araçları ve otomatik yeniden düzenleme aracılığıyla büyük kod tabanlarının kolayca yeniden düzenlenmesine olanak tanır.
  • Türler aracılığıyla ek anlamlar ve açık veri yapıları sağlayarak kod tabanının anlaşılmasını kolaylaştırır.


Ancak kod tabanınızda any türü kullanmaya başladığınız anda yukarıda sıralanan tüm avantajları kaybedersiniz. any tip, tip sisteminde tehlikeli bir boşluktur ve bunun kullanılması, tip kontrolüne bağlı tüm araçların yanı sıra tüm tip kontrolü yeteneklerini de devre dışı bırakır. Sonuç olarak TypeScript'in tüm avantajları kaybolur: hatalar gözden kaçırılır, kod düzenleyiciler daha az kullanışlı hale gelir ve daha fazlası.


Örneğin aşağıdaki örneği göz önünde bulundurun:


 function parse(data: any) { return data.split(''); } // Case 1 const res1 = parse(42); // ^ TypeError: data.split is not a function // Case 2 const res2 = parse('hello'); // ^ any


Yukarıdaki kodda:


  • parse işlevi içindeki otomatik tamamlamayı kaçıracaksınız. data. düzenleyicinizde, data için mevcut yöntemlere ilişkin doğru öneriler size sunulmayacaktır.
  • İlk durumda, bir TypeError: data.split is not a function çünkü bir dize yerine bir sayı ilettik. TypeScript, any tür denetimini devre dışı bıraktığından hatayı vurgulayamıyor.
  • İkinci durumda res2 değişkeni de any türe sahiptir. Bu, any birinin tek bir kullanımının kod tabanının büyük bir kısmı üzerinde basamaklı bir etkiye sahip olabileceği anlamına gelir.


any kullanılması yalnızca aşırı durumlarda veya prototip oluşturma ihtiyaçları için uygundur. Genel olarak TypeScript'ten en iyi şekilde yararlanmak için any kullanmaktan kaçınmak daha iyidir.

Herhangi Bir Türün Geldiği Yer

Bir kod tabanındaki any türün kaynaklarının farkında olmak önemlidir çünkü açıkça any yazmak tek seçenek değildir. any bir türü kullanmaktan kaçınmak için elimizden geleni yapmamıza rağmen, bazen dolaylı olarak kod tabanına gizlice girebilir.


Bir kod tabanında any türden dört ana kaynak vardır:

  1. Tsconfig'deki derleyici seçenekleri.
  2. TypeScript'in standart kütüphanesi.
  3. Proje bağımlılıkları.
  4. any kod tabanında açık kullanımı.


İlk iki nokta için tsconfig'te Önemli Hususlar ve Standart Kütüphane Türlerinin Geliştirilmesi konularında zaten makaleler yazdım. Projelerinizde tip güvenliğini artırmak istiyorsanız lütfen bunlara göz atın.


Bu sefer kod tabanındaki any türün görünümünü kontrol etmeye yönelik otomatik araçlara odaklanacağız.

Aşama 1: ESLint'i kullanma

ESLint , web geliştiricileri tarafından en iyi uygulamaları ve kod biçimlendirmesini sağlamak için kullanılan popüler bir statik analiz aracıdır. Kodlama stillerini uygulamak ve belirli kurallara uymayan kodları bulmak için kullanılabilir.


ESLint, typectipt-eslint eklentisi sayesinde TypeScript projeleriyle de kullanılabilir. Büyük olasılıkla, bu eklenti projenize zaten yüklenmiştir. Ancak değilse resmi başlangıç kılavuzunu takip edebilirsiniz.


typescript-eslint için en yaygın yapılandırma aşağıdaki gibidir:


 module.exports = { extends: [ 'eslint:recommended', 'plugin:@typescript-eslint/recommended', ], plugins: ['@typescript-eslint'], parser: '@typescript-eslint/parser', root: true, };


Bu yapılandırma, eslint TypeScript'i sözdizimi düzeyinde anlamasına olanak tanıyarak, bir kodda manuel olarak yazılan türler için geçerli olan basit eslint kuralları yazmanıza olanak tanır. Örneğin, any şeyin açıkça kullanımını yasaklayabilirsiniz.


recommended ön ayar, kod doğruluğunu iyileştirmeyi amaçlayan, dikkatle seçilmiş bir dizi ESLint kuralını içerir. Ön ayarın tamamının kullanılması tavsiye edilse de, bu makalenin amacı doğrultusunda yalnızca no-explicit-any kuralına odaklanacağız.

açık-hiçbiri

TypeScript'in katı modu, ima edilen any ifadesinin kullanımını engeller, ancak any açıkça kullanılmasını engellemez. no-explicit-any kural, kod tabanında any yere manuel olarak yazılmasını yasaklamaya yardımcı olur.


 // ❌ Incorrect function loadPokemons(): any {} // ✅ Correct function loadPokemons(): unknown {} // ❌ Incorrect function parsePokemons(data: Response<any>): Array<Pokemon> {} // ✅ Correct function parsePokemons(data: Response<unknown>): Array<Pokemon> {} // ❌ Incorrect function reverse<T extends Array<any>>(array: T): T {} // ✅ Correct function reverse<T extends Array<unknown>>(array: T): T {}


Bu kuralın temel amacı takım genelinde any kullanımın engellenmesidir. Bu, ekibin projede any kullanımının caydırılmadığı konusundaki anlaşmasını güçlendirmenin bir yoludur.


Bu çok önemli bir hedeftir çünkü any birinin tek bir kullanımı bile tür çıkarımından dolayı kod tabanının önemli bir kısmı üzerinde basamaklı bir etkiye sahip olabilir. Ancak bu hala nihai tip güvenliğe ulaşmaktan çok uzak.

Neden hiçbir şeyin açık olmaması yeterli değil

Her ne kadar açıkça kullanılan any ile ilgilenmiş olsak da, bir projenin bağımlılıkları içinde npm paketleri ve TypeScript'in standart kütüphanesi de dahil olmak üzere hala birçok ima edilen any vardır.


Herhangi bir projede görülmesi muhtemel olan aşağıdaki kodu göz önünde bulundurun:


 const response = await fetch('https://pokeapi.co/api/v2/pokemon'); const pokemons = await response.json(); // ^? any const settings = JSON.parse(localStorage.getItem('user-settings')); // ^? any


Hem pokemons hem de settings değişkenleri örtülü olarak any türe verildi. Bu durumda ne no-explicit-any ne de TypeScript'in katı modu bizi uyaracaktır. Henüz değil.


Bunun nedeni, response.json() ve JSON.parse() türlerinin TypeScript'in standart kitaplığından gelmesi ve bu yöntemlerin açık any açıklama içermesidir. Değişkenlerimiz için hâlâ manuel olarak daha iyi bir tür belirleyebiliriz ancak standart kitaplıkta bunlardan any birinin yaklaşık 1.200 örneği vardır. any standart kitaplıktan kod tabanımıza gizlice girebileceği tüm durumları hatırlamak neredeyse imkansızdır.


Aynı şey dış bağımlılıklar için de geçerli. Npm'de pek çok kötü yazılmış kitaplık var ve bunların çoğu hala JavaScript ile yazılıyor. Sonuç olarak, bu tür kitaplıkların kullanılması, kod tabanında pek çok örtülü any bulunmasına kolaylıkla yol açabilir.


Genel olarak any kodumuza sızmasının hâlâ birçok yolu vardır.

Aşama 2: Tür Denetimi Yeteneklerinin Geliştirilmesi

İdeal olarak, TypeScript'te derleyicinin herhangi bir nedenle any türü alan herhangi bir değişken hakkında şikayette bulunmasını sağlayan bir ayara sahip olmak isteriz. Maalesef böyle bir ayar şu anda mevcut değil ve eklenmesi de beklenmiyor.


Bu davranışı typescript-eslint eklentisinin type-checked modunu kullanarak başarabiliriz. Bu mod, TypeScript derleyicisinden ESLint kurallarına kadar eksiksiz tür bilgisi sağlamak için TypeScript ile birlikte çalışır. Bu bilgilerle, TypeScript'in tür denetleme yeteneklerini esasen genişleten daha karmaşık ESLint kuralları yazmak mümkündür. Örneğin, bir kural, any nasıl elde edildiğine bakılmaksızın, any türdeki tüm değişkenleri bulabilir.


Yazıma duyarlı kuralları kullanmak için ESLint yapılandırmasını biraz değiştirmeniz gerekir:


 module.exports = { extends: [ 'eslint:recommended', - 'plugin:@typescript-eslint/recommended', + 'plugin:@typescript-eslint/recommended-type-checked', ], plugins: ['@typescript-eslint'], parser: '@typescript-eslint/parser', + parserOptions: { + project: true, + tsconfigRootDir: __dirname, + }, root: true, };


typescript-eslint için tür çıkarımını etkinleştirmek için parserOptions ESLint yapılandırmasına ekleyin. Ardından, recommended ön ayarı recommended-type-checked ile değiştirin. İkinci ön ayar yaklaşık 17 yeni güçlü kural ekler. Bu makalenin amacı doğrultusunda bunlardan yalnızca 5 tanesine odaklanacağız.

güvenli olmayan tartışma yok

no-unsafe-argument kuralı, any türünde bir değişkenin parametre olarak iletildiği işlev çağrılarını arar. Bu olduğunda, yazım denetimi kaybolur ve güçlü yazmanın tüm faydaları da kaybolur.


Örneğin, parametre olarak bir nesne gerektiren bir saveForm fonksiyonunu ele alalım. JSON'u aldığımızı, ayrıştırdığımızı ve any bir tür elde ettiğimizi varsayalım.


 // ❌ Incorrect function saveForm(values: FormValues) { console.log(values); } const formValues = JSON.parse(userInput); // ^? any saveForm(formValues); // ^ Unsafe argument of type `any` assigned // to a parameter of type `FormValues`.


Bu parametreyle saveForm fonksiyonunu çağırdığımızda, no-unsafe-argument kuralı bunu güvensiz olarak işaretler ve value değişkeni için uygun türü belirtmemizi gerektirir.


Bu kural, işlev bağımsız değişkenleri içindeki iç içe geçmiş veri yapılarını derinlemesine inceleyecek kadar güçlüdür. Bu nedenle, nesnelerin işlev bağımsız değişkenleri olarak iletilmesinin hiçbir zaman türlenmemiş veriler içermeyeceğinden emin olabilirsiniz.


 // ❌ Incorrect saveForm({ name: 'John', address: JSON.parse(addressJson), // ^ Unsafe assignment of an `any` value. });


Hatayı düzeltmenin en iyi yolu TypeScript'in tür daraltmasını veya Zod veya Superstruct gibi bir doğrulama kitaplığını kullanmaktır. Örneğin, ayrıştırılan verilerin kesin türünü daraltan parseFormValues fonksiyonunu yazalım.


 // ✅ Correct function parseFormValues(data: unknown): FormValues { if ( typeof data === 'object' && data !== null && 'name' in data && typeof data['name'] === 'string' && 'address' in data && typeof data.address === 'string' ) { const { name, address } = data; return { name, address }; } throw new Error('Failed to parse form values'); } const formValues = parseFormValues(JSON.parse(userInput)); // ^? FormValues saveForm(formValues);


any türün, unknown kabul eden bir işleve bağımsız değişken olarak iletilmesine izin verildiğini unutmayın; çünkü bunu yapmakla ilgili herhangi bir güvenlik sorunu yoktur.


Veri doğrulama işlevlerini yazmak, özellikle büyük miktarda veriyle uğraşırken sıkıcı bir iş olabilir. Bu nedenle, bir veri doğrulama kütüphanesinin kullanılması dikkate alınmalıdır. Örneğin Zod'da kod şöyle görünecektir:


 // ✅ Correct import { z } from 'zod'; const schema = z.object({ name: z.string(), address: z.string(), }); const formValues = schema.parse(JSON.parse(userInput)); // ^? { name: string, address: string } saveForm(formValues);


güvenli olmayan atama yok

no-unsafe-assignment kuralı, bir değerin any türe sahip olduğu değişken atamaları arar. Bu tür atamalar, derleyiciyi bir değişkenin belirli bir türe sahip olduğunu düşünmesine neden olabilirken, veriler aslında farklı bir türe sahip olabilir.


Önceki JSON ayrıştırma örneğini düşünün:


 // ❌ Incorrect const formValues = JSON.parse(userInput); // ^ Unsafe assignment of an `any` value


no-unsafe-assignment kuralı sayesinde, formValues başka bir yere aktarmadan önce bile any türü yakalayabiliriz. Sabitleme stratejisi aynı kalıyor: Değişkenin değerine belirli bir tür sağlamak için tür daraltmayı kullanabiliriz.


 // ✅ Correct const formValues = parseFormValues(JSON.parse(userInput)); // ^? FormValues


güvenli olmayan üye erişimi yok ve güvenli olmayan çağrı yok

Bu iki kural çok daha az sıklıkla tetiklenir. Ancak deneyimlerime dayanarak, kötü yazılmış üçüncü taraf bağımlılıklarını kullanmaya çalıştığınızda bunlar gerçekten faydalıdır.


no-unsafe-member-access kuralı, bir değişkenin any türe sahip olması durumunda nesne özelliklerine erişmemizi engeller; çünkü bu, null veya undefined olabilir.


no-unsafe-call kuralı, any türden bir değişkeni işlev olarak çağırmamızı engeller, çünkü bu bir işlev olmayabilir.


untyped-auth adında kötü yazılmış bir üçüncü taraf kitaplığımızın olduğunu düşünelim:


 // ❌ Incorrect import { authenticate } from 'untyped-auth'; // ^? any const userInfo = authenticate(); // ^? any ^ Unsafe call of an `any` typed value. console.log(userInfo.name); // ^ Unsafe member access .name on an `any` value.


Linter iki konuyu vurgulamaktadır:

  • authenticate işlevinin çağrılması güvenli olmayabilir, çünkü önemli argümanları işleve iletmeyi unutabiliriz.
  • Kimlik doğrulama başarısız olursa null olacağından name özelliğinin userInfo nesnesinden okunması güvenli değildir.


Bu hataları düzeltmenin en iyi yolu, türü kesin olarak belirlenmiş bir API içeren bir kitaplık kullanmayı düşünmektir. Ancak bu bir seçenek değilsekitaplık türlerini kendiniz artırabilirsiniz . Sabit kitaplık türlerine sahip bir örnek şuna benzer:


 // ✅ Correct import { authenticate } from 'untyped-auth'; // ^? (login: string, password: string) => Promise<UserInfo | null> const userInfo = await authenticate('test', 'pwd'); // ^? UserInfo | null if (userInfo) { console.log(userInfo.name); }


güvenli olmayan dönüş yok

no-unsafe-return kuralı, daha spesifik bir şey döndürmesi gereken bir işlevden any türün yanlışlıkla döndürülmemesine yardımcı olur. Bu gibi durumlar, derleyiciyi, döndürülen değerin belirli bir türe sahip olduğunu düşünmesine neden olabilirken, veriler gerçekte farklı bir türe sahip olabilir.


Örneğin, JSON'u ayrıştıran ve iki özelliğe sahip bir nesne döndüren bir fonksiyonumuz olduğunu varsayalım.


 // ❌ Incorrect interface FormValues { name: string; address: string; } function parseForm(json: string): FormValues { return JSON.parse(json); // ^ Unsafe return of an `any` typed value. } const form = parseForm('null'); console.log(form.name); // ^ TypeError: Cannot read properties of null


parseForm fonksiyonu, ayrıştırılan değer kontrol edilmediğinden, kullanıldığı programın herhangi bir bölümünde çalışma zamanı hatalarına neden olabilir. no-unsafe-return kuralı bu tür çalışma zamanı sorunlarını önler.


Ayrıştırılan JSON'un beklenen türle eşleştiğinden emin olmak için doğrulama ekleyerek bunu düzeltmek kolaydır. Bu sefer Zod kütüphanesini kullanalım:


 // ✅ Correct import { z } from 'zod'; const schema = z.object({ name: z.string(), address: z.string(), }); function parseForm(json: string): FormValues { return schema.parse(JSON.parse(json)); }


Performans Hakkında Bir Not

Tür denetimli kuralların kullanılması, ESLint'in tüm türleri çıkarması için TypeScript'in derleyicisini çağırması gerektiğinden performans kaybıyla birlikte gelir. Bu yavaşlama esas olarak linter'ı ön işleme kancalarında ve CI'da çalıştırırken fark edilir, ancak bir IDE'de çalışırken fark edilmez. Tür denetimi, IDE başlangıcında bir kez gerçekleştirilir ve ardından siz kodu değiştirdikçe türleri günceller.


Yalnızca türlerin çıkarımının, tsc derleyicisinin olağan çağrılmasından daha hızlı çalıştığını belirtmekte fayda var. Örneğin, yaklaşık 1,5 milyon satırlık TypeScript kodu içeren en son projemizde, tsc aracılığıyla tür kontrolü yaklaşık 11 dakika sürerken, ESLint'in türe duyarlı kurallarının önyükleme yapması için gereken ek süre yalnızca yaklaşık 2 dakikadır.


Ekibimiz için, türe duyarlı statik analiz kurallarının kullanılmasıyla sağlanan ek güvenlik, ödün vermeye değerdir. Daha küçük projelerde bu kararı vermek daha da kolaydır.

Çözüm

TypeScript projelerinde any birinin kullanımını kontrol etmek, optimum tür güvenliği ve kod kalitesi elde etmek için çok önemlidir. Geliştiriciler typescript-eslint eklentisini kullanarak kod tabanlarındaki any türün oluşumlarını tanımlayabilir ve ortadan kaldırabilir, böylece daha sağlam ve bakımı kolay bir kod tabanı elde edilebilir.


Türe duyarlı eslint kuralları kullanıldığında, any bir anahtar kelimenin kod tabanımızda görünmesi bir hata veya gözden kaçırma yerine kasıtlı bir karar olacaktır. Bu yaklaşım bizi kendi any , standart kütüphanede ve üçüncü taraf bağımlılıklarında kullanmaktan korur.


Genel olarak, türe duyarlı bir linter, Java, Go, Rust ve diğerleri gibi statik olarak yazılan programlama dillerine benzer bir tür güvenliği düzeyi elde etmemizi sağlar. Bu, büyük projelerin geliştirilmesini ve bakımını büyük ölçüde basitleştirir.


Umarım bu makaleden yeni bir şeyler öğrenmişsinizdir. Okuduğunuz için teşekkürler!

kullanışlı bağlantılar