TypeScript'te Dinamik Statik Yazma

Yayınlanan: 2022-03-10
Hızlı özet ↬ Bu makalede, birleşim türleri, koşullu türler, şablon değişmez türleri ve jenerikler gibi TypeScript'in daha gelişmiş özelliklerinden bazılarına bakıyoruz. En dinamik JavaScript davranışını, çoğu hatayı oluşmadan önce yakalayabileceğimiz şekilde resmileştirmek istiyoruz. 2020'nin sonlarında Smashing Magazine'de burada yayınladığımız bir kitap olan 50 Derste TypeScript'in tüm bölümlerinden birkaç öğrenmeyi uyguluyoruz. Daha fazlasını öğrenmek istiyorsanız, kontrol ettiğinizden emin olun!

JavaScript, doğası gereği dinamik bir programlama dilidir. Geliştiriciler olarak bizler çok az çabayla çok şey ifade edebiliriz ve dil ve çalışma zamanı ne yapmak istediğimizi anlıyor. JavaScript'i yeni başlayanlar için bu kadar popüler yapan ve deneyimli geliştiricileri üretken kılan şey budur! Yine de bir uyarı var: Uyanık olmalıyız! Hatalar, yazım hataları, doğru program davranışı: Bunların çoğu kafamızda olur!

Aşağıdaki örneğe bir göz atın.

 app.get("/api/users/:userID", function(req, res) { if (req.method === "POST") { res.status(20).send({ message: "Got you, user " + req.params.userId }); } })

Bir rota (veya yol) tanımlamamıza izin veren ve URL istenirse bir geri arama yürüten bir https://expressjs.com/-style sunucumuz var.

Geri arama iki argüman alır:

  1. request nesnesi.
    Burada kullanılan HTTP yöntemi (örneğin GET, POST, PUT, DELETE) ve gelen ek parametreler hakkında bilgi alıyoruz. Bu örnekte userID , kullanıcının kimliğini içeren bir parametre userID ile eşlenmelidir!
  2. response veya reply nesnesi.
    Burada sunucudan istemciye uygun bir yanıt hazırlamak istiyoruz. Doğru durum kodlarını (yöntem status ) göndermek ve tel üzerinden JSON çıktısı göndermek istiyoruz.

Bu örnekte gördüğümüz şey büyük ölçüde basitleştirilmiştir, ancak neyin peşinde olduğumuz konusunda iyi bir fikir verir. Yukarıdaki örnek de hatalarla dolu! Bir göz atın:

 app.get("/api/users/:userID", function(req, res) { if (req.method === "POST") { /* Error 1 */ res.status(20).send({ /* Error 2 */ message: "Welcome, user " + req.params.userId /* Error 3 */ }); } })

Vay canına! Üç satır uygulama kodu ve üç hata mı? Ne oldu?

  1. İlk hata nüanslıdır. Uygulamamıza GET isteklerini dinlemek istediğimizi söylerken (dolayısıyla app.get ), yalnızca istek yöntemi POST ise bir şeyler yaparız. Uygulamamızın bu özel noktasında, req.method POST olamaz. Bu nedenle, beklenmedik zaman aşımlarına yol açabilecek hiçbir yanıt göndermeyiz.
  2. Açıkça bir durum kodu göndermemiz harika! 20 geçerli bir durum kodu değil. Müşteriler burada neler olduğunu anlamayabilir.
  3. Geri göndermek istediğimiz yanıt budur. Ayrıştırılan argümanlara erişiyoruz ancak ortalama bir yazım hatası var. userID değil userId . Tüm kullanıcılarımız “Hoş geldiniz, tanımsız kullanıcı!” ile karşılanacaktır. Vahşi doğada kesinlikle görmüş olduğunuz bir şey!

Ve böyle şeyler olur! Özellikle JavaScript'te. Anlamlılık kazanırız - bir zamanlar türler hakkında endişelenmek zorunda kalmamıştık - ama ne yaptığımıza çok dikkat etmek zorundaydık.

Bu aynı zamanda JavaScript'in dinamik programlama dillerine alışkın olmayan programcılardan çok fazla tepki aldığı yerdir. Genellikle onları olası sorunlara işaret eden ve hataları önceden yakalayan derleyicileri vardır. Her şeyin doğru çalıştığından emin olmak için kafanızda yapmanız gereken fazladan iş miktarına kaşlarını çattıklarında kibirli görünebilirler. Size JavaScript'in hiçbir türü olmadığını bile söyleyebilirler. Hangisi doğru değil.

TypeScript'in baş mimarı Anders Hejlsberg, MS Build 2017 açılış konuşmasında “ JavaScript'in bir tür sistemine sahip olmaması değil. Sadece resmileştirmenin bir yolu yok ”.

Ve bu TypeScript'in ana amacıdır. TypeScript, JavaScript kodunuzu sizden daha iyi anlamak istiyor. TypeScript'in ne demek istediğinizi anlayamadığı durumlarda, fazladan tür bilgisi sağlayarak yardımcı olabilirsiniz.

Atlamadan sonra daha fazlası! Aşağıdan okumaya devam edin ↓

Temel Yazma

Ve şu anda yapacağımız şey bu. Express tarzı sunucumuzdan get yöntemini alalım ve mümkün olduğunca çok sayıda hata kategorisini dışlayabilmemiz için yeterli tür bilgisi ekleyelim.

Bazı temel tip bilgileriyle başlıyoruz. get işlevine işaret eden bir app nesnemiz var. get işlevi, bir dize olan path ve bir geri arama alır.

 const app = { get, /* post, put, delete, ... to come! */ }; function get(path: string, callback: CallbackFn) { // to be implemented --> not important right now }

string temel, sözde ilkel bir tür olsa da, CallbackFn açıkça tanımlamamız gereken bir bileşik türdür.

CallbackFn , iki bağımsız değişken alan bir işlev türüdür:

  • ServerRequest türünde olan req
  • ServerReply türündeki reply

CallbackFn void döndürür.

 type CallbackFn = (req: ServerRequest, reply: ServerReply) => void;

ServerRequest , çoğu çerçevede oldukça karmaşık bir nesnedir. Gösteri amacıyla basitleştirilmiş bir sürüm yapıyoruz. "GET" , "POST" , "PUT" , "DELETE" vb. için bir method dizesi iletiyoruz. Ayrıca bir params kaydı da var. Kayıtlar, bir dizi özellik ile bir dizi anahtarı ilişkilendiren nesnelerdir. Şimdilik, her string anahtarının bir string özelliğine eşlenmesine izin vermek istiyoruz. Bunu daha sonra yeniden değerlendiririz.

 type ServerRequest = { method: string; params: Record<string, string>; };

ServerReply için, gerçek bir ServerReply nesnesinin çok daha fazlasına sahip olduğunu bilerek bazı işlevleri düzenleriz. send işlevi, göndermek istediğimiz verilerle isteğe bağlı bir argüman alır. Ve status fonksiyonu ile bir durum kodu belirleme imkanımız var.

 type ServerReply = { send: (obj?: any) => void; status: (statusCode: number) => ServerReply; };

Bu zaten bir şey ve birkaç hatayı ekarte edebiliriz:

 app.get("/api/users/:userID", function(req, res) { if(req.method === 2) { // ^^^^^^^^^^^^^^^^^ Error, type number is not assignable to string res.status("200").send() // ^^^^^ Error, type string is not assignable to number } })

Ancak yine de yanlış durum kodları gönderebiliriz (herhangi bir sayı mümkündür) ve olası HTTP yöntemleri hakkında hiçbir fikrimiz yoktur (herhangi bir dize mümkündür). Türlerimizi geliştirelim.

Küçük Setler

İlkel türleri, o belirli kategorinin tüm olası değerlerinin bir kümesi olarak görebilirsiniz. Örneğin, string , JavaScript'te ifade edilebilecek tüm olası dizeleri içerir, number , çift kayan nokta kesinliği ile tüm olası sayıları içerir. boolean , true ve false olan tüm olası boole değerlerini içerir.

TypeScript, bu kümeleri daha küçük alt kümelere daraltmanıza olanak tanır. Örneğin, HTTP yöntemleri için alabileceğimiz tüm olası dizeleri içeren bir Method türü oluşturabiliriz:

 type Methods= "GET" | "POST" | "PUT" | "DELETE"; type ServerRequest = { method: Methods; params: Record<string, string>; };

Method , daha büyük string kümesinin daha küçük bir kümesidir. Method , değişmez türlerin birleşim türüdür. Bir hazır bilgi türü, belirli bir kümenin en küçük birimidir. Gerçek bir dize. Gerçek bir sayı. Belirsizlik yok. Sadece "GET" . Sahip olduğunuz daha büyük türlerin bir alt kümesini oluşturarak, onları diğer değişmez türlerle birleştirirsiniz. Ayrıca hem string hem de number değişmez türlerini veya farklı bileşik nesne türlerini içeren bir alt küme yapabilirsiniz. Değişmez türleri birleştirmek ve birleşimlere koymak için birçok olasılık vardır.

Bunun sunucu geri aramamız üzerinde hemen bir etkisi vardır. Aniden, bu dört yöntemi (veya gerekirse daha fazlasını) ayırt edebiliriz ve koddaki tüm olasılıkları tüketebiliriz. TypeScript bize rehberlik edecek:

 app.get("/api/users/:userID", function (req, res) { // at this point, TypeScript knows that req.method // can take one of four possible values switch (req.method) { case "GET": break; case "POST": break; case "DELETE": break; case "PUT": break; default: // here, req.method is never req.method; } });

TypeScript, yaptığınız her case bildiriminde size mevcut seçenekler hakkında bilgi verebilir. Kendiniz deneyin. Tüm seçenekleri tükettiyseniz, TypeScript default şubenizde bunun never size söyleyecektir. Bu, kelimenin tam anlamıyla never türündendir; bu, muhtemelen ele almanız gereken bir hata durumuna ulaştığınız anlamına gelir.

Bu bir hata kategorisi daha azdır. Artık tam olarak hangi olası HTTP yöntemlerinin mevcut olduğunu biliyoruz.

Aynısını, statusCode alabileceği geçerli sayıların bir alt kümesini tanımlayarak HTTP durum kodları için de yapabiliriz:

 type StatusCode = 100 | 101 | 102 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 226 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 420 | 422 | 423 | 424 | 425 | 426 | 428 | 429 | 431 | 444 | 449 | 450 | 451 | 499 | 500 | 501 | 502 | 503 | 504 | 505 | 506 | 507 | 508 | 509 | 510 | 511 | 598 | 599; type ServerReply = { send: (obj?: any) => void; status: (statusCode: StatusCode) => ServerReply; };

Type StatusCode yine bir birlik türüdür. Ve bununla birlikte, başka bir hata kategorisini hariç tutuyoruz. Aniden, böyle bir kod başarısız olur:

 app.get("/api/user/:userID", (req, res) => { if(req.method === "POS") { // ^^^^^^^^^^^^^^^^^^^ 'Methods' and '"POS"' have no overlap. res.status(20) // ^^ '20' is not assignable to parameter of type 'StatusCode' } })
Ve yazılımımız çok daha güvenli hale geliyor! Ama daha fazlasını yapabiliriz!

Jenerik Girin

app.get ile bir rota tanımladığımızda, mümkün olan tek HTTP yönteminin "GET" olduğunu dolaylı olarak biliyoruz. Ancak tür tanımlarımızla birlikte, birliğin tüm olası bölümlerini kontrol etmemiz gerekiyor.

Tüm olası HTTP yöntemleri için geri arama işlevlerini tanımlayabileceğimizden, CallbackFn türü doğrudur, ancak açıkça app.get , yalnızca yazımlara uymak için gerekli olan bazı ekstra adımları kaydetmek güzel olurdu.

TypeScript jenerikleri yardımcı olabilir! Jenerikler, TypeScript'teki statik türlerden en dinamik davranışı elde etmenize izin veren ana özelliklerden biridir. 50 Derste TypeScript'te, son üç bölümü jeneriklerin tüm inceliklerini ve benzersiz işlevlerini inceleyerek geçiriyoruz.

Şu anda bilmeniz gereken şu ki, Methods tüm set yerine ServerRequest bir kısmını belirtebileceğimiz şekilde tanımlamak istiyoruz. Bunun için, fonksiyonlarda yaptığımız gibi parametreleri tanımlayabileceğimiz genel söz dizimini kullanıyoruz:

 type ServerRequest<Met extends Methods> = { method: Met; params: Record<string, string>; };

Bu olur:

  1. ServerRequest , köşeli parantezlerle belirtildiği gibi genel bir tür haline gelir
  2. Methods türünün bir alt kümesi olan Met adlı genel bir parametre tanımlarız.
  3. Yöntemi tanımlamak için bu genel parametreyi genel bir değişken olarak kullanırız.

Ayrıca, genel parametreleri adlandırma konusundaki makaleme göz atmanızı da tavsiye ederim.

Bu değişiklikle, şeyleri kopyalamadan farklı ServerRequest s belirtebiliriz:

 type OnlyGET = ServerRequest<"GET">; type OnlyPOST = ServerRequest<"POST">; type POSTorPUT = ServerRquest<"POST" | "PUT">;

ServerRequest arayüzünü değiştirdiğimiz için, CallbackFn ve get işlevi gibi ServerRequest kullanan diğer tüm türlerimizde değişiklik yapmamız gerekiyor:

 type CallbackFn<Met extends Methods> = ( req: ServerRequest<Met>, reply: ServerReply ) => void; function get(path: string, callback: CallbackFn<"GET">) { // to be implemented }

get işleviyle, genel türümüze gerçek bir argüman iletiyoruz. Bunun yalnızca Methods alt kümesi olmayacağını biliyoruz, tam olarak hangi alt kümeyle uğraştığımızı biliyoruz.

Şimdi, app.get kullandığımızda, sadece req.method için olası değere sahibiz:

 app.get("/api/users/:userID", function (req, res) { req.method; // can only be get });

Bu, bir app.get geri çağrısı oluşturduğumuzda "POST" veya benzeri gibi HTTP yöntemlerinin mevcut olduğunu varsaymamamızı sağlar. Bu noktada tam olarak neyle uğraştığımızı biliyoruz, o yüzden bunu türlerimize yansıtalım.

request.method öğesinin makul bir şekilde yazıldığından ve gerçek durumu temsil ettiğinden emin olmak için zaten çok şey yaptık. Methods union tipini alt kümelemeyle elde ettiğimiz güzel bir fayda, app.get dışında tip açısından güvenli bir genel amaçlı geri arama işlevi oluşturabilmemizdir:

 const handler: CallbackFn<"PUT" | "POST"> = function(res, req) { res.method // can be "POST" or "PUT" }; const handlerForAllMethods: CallbackFn<Methods> = function(res, req) { res.method // can be all methods }; app.get("/api", handler); // ^^^^^^^ Nope, we don't handle "GET" app.get("/api", handlerForAllMethods); // This works

Parametreleri Yazma

Henüz dokunmadığımız şey params nesnesini yazmak. Şimdiye kadar, her string anahtarına erişmeye izin veren bir kayıt elde ettik. Şimdi bunu biraz daha belirgin hale getirmek bizim görevimiz!

Bunu başka bir genel değişken ekleyerek yaparız. Biri yöntemler için, biri Record olası anahtarlar için:

 type ServerRequest<Met extends Methods, Par extends string = string> = { method: Met; params: Record<Par, string>; };

Genel tür değişkeni Par , string türünün bir alt kümesi olabilir ve varsayılan değer her dizedir. Bununla, ServerRequest hangi anahtarları beklediğimizi söyleyebiliriz:

 // request.method = "GET" // request.params = { // userID: string // } type WithUserID = ServerRequest<"GET", "userID">

Yeni argümanı get fonksiyonumuza ve CallbackFn tipine ekleyelim, böylece istenen parametreleri ayarlayabiliriz:

 function get<Par extends string = string>( path: string, callback: CallbackFn<"GET", Par> ) { // to be implemented } type CallbackFn<Met extends Methods, Par extends string> = ( req: ServerRequest<Met, Par>, reply: ServerReply ) => void;

Par Par olarak string olduğundan, tür alıştığımız gibi çalışır. Yine de ayarlarsak, aniden req.params nesnesi için uygun bir tanımımız olur!

 app.get<"userID">("/api/users/:userID", function (req, res) { req.params.userID; // Works!! req.params.anythingElse; // doesn't work!! });

Bu harika! Yine de geliştirilebilecek küçük bir şey var. Yine de her dizgiyi app.get path argümanına iletebiliriz. Par oraya da yansıtsak daha iyi olmaz mı?

Yapabiliriz! 4.1 sürümünün piyasaya sürülmesiyle TypeScript, şablon değişmez türleri oluşturabilir. Sözdizimsel olarak, dize şablonu değişmezleri gibi çalışırlar, ancak tür düzeyinde. Küme string dizge değişmez türleriyle alt kümelere ayırabildiğimiz yerde (Yöntemlerde yaptığımız gibi), şablon değişmez türleri, tüm bir dizi dizisini dahil etmemize izin verir.

Parametre adının önüne iki nokta üst üste eklemenin Ekspres stilinde Par düzgün bir şekilde dahil edildiğinden emin olmak istediğimiz IncludesRouteParams adında bir tür oluşturalım:

 type IncludesRouteParams<Par extends string> = | `${string}/:${Par}` | `${string}/:${Par}/${string}`;

Genel tür IncludesRouteParams , bir string alt kümesi olan bir bağımsız değişken alır. İki şablon değişmezinin birleşim türünü oluşturur:

  1. İlk şablon değişmezi herhangi bir string ile başlar, ardından bir / karakterini ve ardından bir : karakterini ve ardından parametre adını içerir. Bu, parametrenin rota dizesinin sonunda olduğu tüm durumları yakalamamızı sağlar.
  2. İkinci şablon değişmezi herhangi bir string ile başlar, ardından aynı / , : kalıbı ve parametre adı gelir. Sonra başka bir / karakterimiz var, ardından herhangi bir dize. Birleşim türünün bu dalı, parametrenin bir rota içinde bir yerde olduğu tüm durumları yakalamamızı sağlar.

UserID parametre adına sahip IncludesRouteParams , farklı test userID şu şekilde davranır:

 const a: IncludeRouteParams<"userID"> = "/api/user/:userID" // const a: IncludeRouteParams<"userID"> = "/api/user/:userID/orders" // const a: IncludeRouteParams<"userID"> = "/api/user/:userId" // const a: IncludeRouteParams<"userID"> = "/api/user" // const a: IncludeRouteParams<"userID"> = "/api/user/:userIDAndmore" //

Yeni yardımcı program türümüzü get işlev bildirimine dahil edelim.

 function get<Par extends string = string>( path: IncludesRouteParams<Par>, callback: CallbackFn<"GET", Par> ) { // to be implemented } app.get<"userID">( "/api/users/:userID", function (req, res) { req.params.userID; // YEAH! } );

Harika! Parametreleri gerçek rotaya eklemeyi kaçırmadığımızdan emin olmak için başka bir güvenlik mekanizması alıyoruz! Ne kadar güçlü.

Genel bağlamalar

Ama tahmin edin, ben hala bundan memnun değilim. Bu yaklaşımla ilgili, rotalarınız biraz daha karmaşık hale geldiği anda ortaya çıkan birkaç sorun var.

  1. Sahip olduğum ilk sorun, parametrelerimizi genel tür parametresinde açıkça belirtmemiz gerektiğidir. Par "userID" bağlamalıyız, yine de işlevin yol argümanında belirtecek olsak bile. Bu JavaScript-y değil!
  2. Bu yaklaşım yalnızca bir rota parametresini işler. Bir birleşim eklediğimiz an, örneğin "userID" | "orderId" "userID" | "orderId" failsafe denetimi, bu bağımsız değişkenlerden yalnızca birinin mevcut olmasıyla karşılanır. Setler böyle çalışır. Biri veya diğeri olabilir.

Daha iyi bir yol olmalı. Ve orada. Aksi takdirde, bu makale çok acı bir notla bitecekti.

Sıralamayı tersine çevirelim! Genel türde bir değişkende rota paramlarını tanımlamaya çalışmayalım, bunun yerine değişkenleri app.get ilk argümanı olarak ilettiğimiz path çıkaralım.

Gerçek değere ulaşmak için, TypeScript'te genel bağlamanın nasıl çalıştığını görmeliyiz. Örneğin bu identity işlevini ele alalım:

 function identity<T>(inp: T) : T { return inp }

Şimdiye kadar gördüğünüz en sıkıcı genel işlev olabilir, ancak bir noktayı mükemmel bir şekilde gösterir. identity bir argüman alır ve aynı girişi tekrar döndürür. Tür, T genel türüdür ve aynı türü döndürür.

Şimdi T string bağlayabiliriz, örneğin:

 const z = identity<string>("yes"); // z is of type string

Bu açıkça genel bağlama, yalnızca identity strings sağlar ve açıkça bağladığımız için, dönüş türü de string . Bağlamayı unutursak ilginç bir şey olur:

 const y = identity("yes") // y is of type "yes"

Bu durumda, TypeScript, ilettiğiniz bağımsız değişkenden türü çıkarır ve T "yes" dizgi değişmez türüne bağlar. Bu, bir işlev argümanını daha sonra diğer genel türlerimizde kullandığımız değişmez bir türe dönüştürmenin harika bir yoludur.

Bunu app.get uyarlayarak yapalım.

 function get<Path extends string = string>( path: Path, callback: CallbackFn<"GET", ParseRouteParams<Path>> ) { // to be implemented }

Par jenerik türünü kaldırıyoruz ve Path ekliyoruz. Path , herhangi bir string alt kümesi olabilir. Path path bu, get öğesine bir parametre ilettiğimiz an, dize değişmez türünü yakaladığımız anlamına gelir. Path henüz oluşturmadığımız yeni bir genel tür ParseRouteParams .

ParseRouteParams üzerinde çalışalım. Burada olayların sırasını tekrar değiştiriyoruz. Yolun doğru olduğundan emin olmak için istenen rota paramlarını jeneriğe geçirmek yerine, rota yolunu geçer ve olası rota paramlarını çıkarırız. Bunun için koşullu bir tür oluşturmamız gerekiyor.

Koşullu Türler ve Özyinelemeli Şablon Değişmez Türleri

Koşullu türler, JavaScript'teki üçlü operatöre sözdizimsel olarak benzer. Bir koşulu kontrol edersiniz ve koşul karşılanırsa A şubesini iade edersiniz, aksi halde B şubesini iade edersiniz. Örneğin:

 type ParseRouteParams<Rte> = Rte extends `${string}/:${infer P}` ? P : never;

Burada, Rte Express stilinin sonundaki parametre ile biten her yolun bir alt kümesi olup olmadığını kontrol ederiz (öncesinde "/:" ile). Eğer öyleyse, bu dizgiyi çıkarırız. Bu, içeriğini yeni bir değişkende yakaladığımız anlamına gelir. Koşul karşılanırsa, yeni çıkarılan dizeyi döndürürüz, aksi takdirde, "Rota parametresi yok" örneğindeki gibi hiçbir zaman döndürmeyiz,

Bunu denersek, şöyle bir şey elde ederiz:

 type Params = ParseRouteParams<"/api/user/:userID"> // Params is "userID" type NoParams = ParseRouteParams<"/api/user"> // NoParams is never --> no params!

Harika, bu zaten daha önce yaptığımızdan çok daha iyi. Şimdi, diğer tüm olası parametreleri yakalamak istiyoruz. Bunun için bir koşul daha eklemeliyiz:

 type ParseRouteParams<Rte> = Rte extends `${string}/:${infer P}/${infer Rest}` ? P | ParseRouteParams<`/${Rest}`> : Rte extends `${string}/:${infer P}` ? P : never;

Koşullu tipimiz artık şu şekilde çalışıyor:

  1. İlk durumda, rota arasında bir yerde bir rota parametresi olup olmadığını kontrol ederiz. Eğer öyleyse, hem route parametresini hem de ondan sonra gelen her şeyi çıkarırız. Yeni bulunan rota parametresi P , Rest ile aynı genel türü yinelemeli olarak adlandırdığımız bir birlik içinde döndürürüz. Örneğin, "/api/users/:userID/orders/:orderID" yolunu ParseRouteParams , "userID "userID" yi P ve "orders/:orderID" yi Rest çıkartırız. Rest ile aynı tip diyoruz
  2. İşte burada ikinci koşul devreye giriyor. Burada sonunda bir tür olup olmadığını kontrol ediyoruz. "orders/:orderID" için durum budur. "orderID" ve bu değişmez türü döndürürüz.
  3. Eğer daha fazla route parametresi kalmamışsa, asla dönmüyoruz.

Dan Vanderkam, ParseRouteParams için benzer ve daha ayrıntılı bir tür gösterir, ancak yukarıda gördüğünüz de çalışmalıdır. Yeni uyarlanmış ParseRouteParams , şöyle bir şey elde ederiz:

 // Params is "userID" type Params = ParseRouteParams<"/api/user/:userID"> // MoreParams is "userID" | "orderID" type MoreParams = ParseRouteParams<"/api/user/:userID/orders/:orderId">

Bu yeni türü uygulayalım ve app.get son kullanımımızın nasıl göründüğünü görelim.

 app.get("/api/users/:userID/orders/:orderID", function (req, res) { req.params.userID; // YES!! req.params.orderID; // Also YES!!! });

Vay. Bu, başlangıçta sahip olduğumuz JavaScript koduna benziyor!

Dinamik Davranış İçin Statik Tipler

Bir işlev app.get için az önce oluşturduğumuz türler, bir ton olası hatayı hariç tuttuğumuzdan emin olur:

  1. res.status() 'a sadece uygun sayısal durum kodlarını iletebiliriz.
  2. req.method dört olası dizgeden biridir ve app.get kullandığımızda bunun yalnızca "GET" olduğunu biliyoruz.
  3. Rota parametrelerini ayrıştırabilir ve geri aramamızda herhangi bir yazım hatası olmadığından emin olabiliriz.

Bu makalenin başındaki örneğe bakarsak aşağıdaki hata mesajlarını alıyoruz:

 app.get("/api/users/:userID", function(req, res) { if (req.method === "POST") { // ^^^^^^^^^^^^^^^^^^^^^ // This condition will always return 'false' // since the types '"GET"' and '"POST"' have no overlap. res.status(20).send({ // ^^ // Argument of type '20' is not assignable to // parameter of type 'StatusCode' message: "Welcome, user " + req.params.userId // ^^^^^^ // Property 'userId' does not exist on type // '{ userID: string; }'. Did you mean 'userID'? }); } })

Ve tüm bunlar kodumuzu çalıştırmadan önce! Ekspres tarzı sunucular, JavaScript'in dinamik doğasının mükemmel bir örneğidir. Çağırdığınız yönteme, ilk argüman için ilettiğiniz dizeye bağlı olarak, geri çağırma içinde birçok davranış değişir. Başka bir örnek alın ve tüm türleriniz tamamen farklı görünüyor.

Ancak birkaç iyi tanımlanmış tür ile kodumuzu düzenlerken bu dinamik davranışı yakalayabiliriz. Çalışma zamanında değil, statik türlerle derleme zamanında!

Ve bu, TypeScript'in gücüdür. Hepimizin çok iyi bildiği tüm dinamik JavaScript davranışlarını resmileştirmeye çalışan statik tip bir sistem. Az önce oluşturduğumuz örneği denemek istiyorsanız, TypeScript oyun alanına gidin ve onunla uğraşın.


50 Derste TypeScript, Stefan Baumgartner Bu yazımızda pek çok kavrama değindik. Daha fazlasını öğrenmek istiyorsanız, küçük, kolay sindirilebilir derslerde yazı sistemine nazik bir giriş yaptığınız 50 Derste TypeScript'e göz atın. E-kitap sürümleri hemen mevcuttur ve basılı kitap, kodlama kitaplığınız için harika bir referans olacaktır.