TypeScript'te Dinamik Statik Yazma
Yayınlanan: 2022-03-10JavaScript, 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:
-
request
nesnesi.
Burada kullanılan HTTP yöntemi (örneğin GET, POST, PUT, DELETE) ve gelen ek parametreler hakkında bilgi alıyoruz. Bu örnekteuserID
, kullanıcının kimliğini içeren bir parametreuserID
ile eşlenmelidir! -
response
veyareply
nesnesi.
Burada sunucudan istemciye uygun bir yanıt hazırlamak istiyoruz. Doğru durum kodlarını (yöntemstatus
) 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?
- İ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. - Açıkça bir durum kodu göndermemiz harika!
20
geçerli bir durum kodu değil. Müşteriler burada neler olduğunu anlamayabilir. - 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ğiluserId
. 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.
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 olanreq
-
ServerReply
türündekireply
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:
-
ServerRequest
, köşeli parantezlerle belirtildiği gibi genel bir tür haline gelir -
Methods
türünün bir alt kümesi olanMet
adlı genel bir parametre tanımlarız. - 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:
- İ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. - İ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.
- 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! - 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:
- İ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"
yolunuParseRouteParams
, "userID"userID"
yiP
ve"orders/:orderID"
yiRest
çıkartırız.Rest
ile aynı tip diyoruz - İş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. - 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:
-
res.status()
'a sadece uygun sayısal durum kodlarını iletebiliriz. -
req.method
dört olası dizgeden biridir veapp.get
kullandığımızda bunun yalnızca"GET"
olduğunu biliyoruz. - 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.
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.