ბიბლიოთეკა
00/07 · ~32 წთ
GUIDEDECK · ბაგების დაჭერა რელიზამდე

TypeScript — ტიპები, რომლებიც
აღწერს და იცავს
თქვენს კოდს.

32-წუთიანი სამუშაო სესია იმ ტიპების სისტემაზე, რომელიც თანამედროვე JavaScript-ის უკან დგას — იმით დაწყებული, თუ რატომ ამართლებს ტიპები, იუნიონებით, ჯენერიკებითა და utility ტიპებით გავლით, მკაცრ რეჟიმამდე და იმ ადგილებამდე, სადაც ტიპები ჩუმად ცრუობს.

~32 წთდამწყები → საშუალოTypeScript 5+
გადაახვიეთ
01 · რატომ ტიპები 3 წთ

დაიჭირეთ ბაგების მთელი კლასები
მანამ, სანამ კოდი გაეშვება.

JavaScript მხოლოდ მაშინ გეუბნებათ, რომ რაღაც არ არის რიგზე, როცა მომხმარებელი მას პროდაქშენში წააწყდება. TypeScript ამ უკუკავშირს წერის მომენტში გადმოაქვს — კითხულობს თქვენს კოდს, ამოწმებს ყოველ მნიშვნელობას იმ ფორმასთან, რომელიც დაჰპირდით, და შეუსაბამობას რედაქტორშივე გაჩვენებთ. იგივე ჩანაწერი ამავე დროს ყოველთვის ზუსტ დოკუმენტაციადაც მუშაობს.

TypeScript — JavaScript-ის ტიპიზებული ზესიმრავლე — ეს არის ჩვეულებრივი JS პლუს არასავალდებულო სტატიკური ტიპების შრე. ცალკე პროგრამა ამ შრეს ამოწმებს მანამ, სანამ თქვენი კოდი გაეშვება, შემდეგ კი შლის მას და ჩვეულებრივ JavaScript-ს გამოსცემს. ტიპებიდან რანტაიმში არაფერი არსებობს — ისინი ინსტრუმენტია თქვენთვის და თქვენი რედაქტორისთვის, არა ძრავისთვის.

ორი საქმე, ორი ინსტრუმენტი

  • ტიპების შემოწმება — შემმოწმებელი კითხულობს .ts ფაილებს და ამტკიცებს, რომ ყოველი მნიშვნელობა თავის გამოცხადებულ ფორმას ემთხვევა. შეუსაბამობები ის შეცდომებია, რომლებსაც რედაქტირებისასვე ხედავთ.
  • ტრანსპილაცია — იმავე წყაროს ტიპები ეხსნება და .js-ად იქცევა, რომელსაც ბრაუზერები და Node უშვებენ.
  • მთავარია: ეს შეიძლება სხვადასხვა ინსტრუმენტი იყოს — და ტიპები რეალურად მხოლოდ შემმოწმებელს ესმის (ნაწილი 6).

შემმოწმებელი თქვენს ტიპებს წინასწარ ამტკიცებს; გამოცემულ JavaScript-ს მათგან არაფერი მიჰყვება. დაიმახსოვრეთ ეს სხვაობა — ნაწილი 7 სწორედ იმაზეა, სად გტკენთ ის.

რას იღებთ სამაგიეროდ

0

აკრეფის შეცდომა რანტაიმში. user.naem შეცდომაა თქვენს რედაქტორში და არა კრახი პროდაქშენში.

↻

უშიშარი რეფაქტორინგი. გადაარქვით ველს სახელი და შემმოწმებელი ჩამოგითვლით ყველა ადგილს, სადაც განახლებაა საჭირო.

📖

ცოცხალი დოკუმენტაცია. ტიპის ხელმოწერა ხსნის, რას ითხოვს და რას აბრუნებს ფუნქცია — და ვერასდროს მოძველდება.

⌨

ნამდვილი ავტოშევსება. რედაქტორმა ფორმა იცის, ამიტომ სწორ ველებს გთავაზობთ და არასწორებს იჭერს.

JavaScript — ვარდება პროდაქშენში
function total(cart) {
  return cart.reduce((sum, i) => sum + i.prce, 0)
}
// typo "prce" → every total becomes NaN
// nothing complains until a user checks out
TypeScript — დაიჭირა წერისასვე
type Item = { price: number }
function total(cart: Item[]): number {
  return cart.reduce((sum, i) => sum + i.prce, 0)
}            // ✕ Property 'prce' does not exist on 'Item'

TypeScript-ს აქ უკვე ყველგან ხვდებით — მასზე დგას Modern React-ის კომპონენტები და State Management-ის სთორები, და ის ბუნებრივი ენაა OOP & Architecture-ის დიზაინის იდეებისთვის. ეს დეკი სწორედ ის ტიპების სისტემაა, რომელსაც ის დეკები ეყრდნობა.

02 · საფუძვლები 5 წთ

TypeScript მნიშვნელობებს მათი
ფორმით აფასებს და არა სახელით.

პირველი გასაკვირი ახალბედისთვის: ტიპები არ ედრება იმის მიხედვით, საიდან მოვიდნენ, არამედ იმის მიხედვით, როგორ გამოიყურებიან. თუ მნიშვნელობას აქვს ის ველები, რომლებიც ფუნქციას სჭირდება, ის ერგება — არც მემკვიდრეობა და არც იარლიყი არაა საჭირო. ეს არის სტრუქტურული ტიპიზაცია, და სწორედ ის აქცევს სისტემას მსუბუქად და JavaScript-ისთვის ბუნებრივად.

სტრუქტურული ტიპიზაცია — მას კომპილაციისას შემოწმებულ duck typing-საც უწოდებენ — ნიშნავს, რომ ორი ტიპი თავსებადია, როცა მათი ფორმები ემთხვევა. მნიშვნელობა ტიპს მიესადაგება, თუ მას სულ მცირე სავალდებულო თვისებები აქვს, თავსებადი ტიპებით. კლასის ან ინტერფეისის სახელს მნიშვნელობა არ აქვს; მხოლოდ სტრუქტურა ითვლება.

ყველაფერი, რასაც x და y აქვს, Point-ია — ზედმეტი ველები არაფერს შველის და არც აფუჭებს; აკლდეს კი არ შეიძლება.

სამშენებლო ბლოკები

  • პრიმიტივები — string, number, boolean, null, undefined, პლუს bigint და symbol.
  • ობიექტები — დასახელებული ველების ჩანაწერი, თითოეული თავისი ტიპით; არასავალდებულო ველები ?-ით აღინიშნება.
  • მასივები & ტუპლები — number[] სიისთვის, [string, number] ფიქსირებული წყვილისთვის.
  • ფუნქციები — ტიპდება პარამეტრებითა და დასაბრუნებელი მნიშვნელობით.
პრიმიტივები

ატომები

მიაწერეთ ცვლადს ტიპი :-ით, ან მიეცით TypeScript-ს საშუალება, თავად დაასკვნას ის მნიშვნელობიდან — ჩვეულებრივ სჯობს, თავად დაასკვნას.

let name: string = "Ada"
let age = 42       // inferred: number
let on: boolean = true
ობიექტები

დასახელებული ფორმები

interface და type ორივე ასახელებს ფორმას; აირჩიეთ ერთი და თანმიმდევრული იყავით. ? არასავალდებულო ველს აღნიშნავს.

interface User {
  id: number
  name: string
  bio?: string  // optional
}
მასივები

სიები & ტუპლები

ერთგვაროვანი სია ერთი მხრივ და ფიქსირებული სიგრძის, პოზიციური ტუპლი მეორე მხრივ — თანმიმდევრობასა და რაოდენობას მნიშვნელობა აქვს.

const ids: number[] = [1, 2]
const pair: [string, number]
  = ["a", 1]
ფუნქციები

ტიპიზებული კონტრაქტები

პარამეტრები და დასაბრუნებელი ტიპი ქმნის კონტრაქტს; მასთან მოწმდება როგორც გამომძახებლები, ისე თავად სხეული.

function greet(
  n: string
): string {
  return `Hi ${n}`
}

interface თუ type — რომელს მივმართოთ

  • interface აღწერს ობიექტების ფორმებს და შეიძლება გაფართოვდეს და შეერწყას — კარგი ნაგულისხმევი არჩევანი საჯარო ობიექტური კონტრაქტებისა და კლასების ფორმებისთვის.
  • type არის მეტსახელი ნებისმიერი ტიპისთვის — იუნიონები, ინტერსექციები, ტუპლები, მაპირებული და პირობითი ტიპები (შემდეგი ნაწილები) ყველა მას საჭიროებს.
  • ჩვეულებრივ ობიექტებზე ისინი ძლიერ ემთხვევა ერთმანეთს. აირჩიეთ სახლის სტილი; type-ს მაშინვე მიმართეთ, როგორც კი მარტივი ობიექტის გარდა სხვა რამ დაგჭირდებათ.
03 · იუნიონები, ინტერსექციები, დავიწროება 5 წთ

აღწერეთ "ეს ან ის" — და მერე მიეცით
კომპილატორს საშუალება, დაამტკიცოს, რომელი გაქვთ.

რეალური მნიშვნელობები ხშირად რამდენიმე ფორმიდან ერთ-ერთია: მოთხოვნა იტვირთება, ან ჩაიტვირთა, ან ჩავარდა. იუნიონი ზუსტად ამას აღწერს. მოგება დავიწროებაა: if-ის ან switch-ის შიგნით TypeScript აკვირდება, რა შეამოწმეთ, და შესაბამისი წევრების უსაფრთხოდ გამოყენების საშუალებას გაძლევთ.

იუნიონი (A | B) — მნიშვნელობა, რომელიც რამდენიმე ტიპიდან ერთ-ერთია. ინტერსექცია (A & B) — მნიშვნელობა, რომელიც ერთდროულად ყველა ჩამოთვლილ ტიპს აკმაყოფილებს (მათ ველებს აერთიანებს). დავიწროება ის გზაა, რომლითაც რანტაიმის შემოწმება (მაგალითად typeof) კომპილატორს ეუბნება, ამჟამად იუნიონის რომელი წევრი გიჭირავთ.

მცველის გარეთ x მთელი იუნიონია; თითოეული განშტოების შიგნით კი ერთი კონკრეტული ტიპია, ამ ტიპის მეთოდებით.

დავიწროების ოთხი გზა

  • typeof — პრიმიტივებისთვის: typeof x === "string".
  • instanceof — კლასებისთვის: err instanceof Error.
  • in — ობიექტებისთვის: "radius" in shape ირჩევს იმ წევრს, რომელსაც ეს ველი აქვს.
  • საკუთარი მცველი — ფუნქცია, რომელიც x is Cat-ს აბრუნებს, კომპილატორს თქვენს წესს ასწავლის.

დისკრიმინირებული იუნიონები — მთავარი სამუშაო პატერნი

მიეცით იუნიონის ყოველ წევრს საერთო ლიტერალური ტეგის ველი. switch ამ ტეგზე იდეალურად ავიწროებს, კომპილატორს კი შეუძლია დაამტკიცოს, რომ ყველა შემთხვევა დაამუშავეთ.

type Shape =
  | { kind: "circle"; r: number }
  | { kind: "square"; size: number }

function area(s: Shape): number {
  switch (s.kind) {              // the tag
    case "circle": return Math.PI * s.r ** 2
    case "square": return s.size ** 2
  }
}                                // add a kind → compiler flags missing case

ერთი ტეგის ველი თითოეულ განშტოებას ზუსტად ერთ ფორმაზე მიმართავს — და სისრულის შემოწმებასაც ის ასაზრდოებს.

სისრულე never-ით

დაამატეთ default, რომელიც მნიშვნელობას never-ს ანიჭებს. თუ მოგვიანებით იუნიონს ახალ წევრს დაამატებთ და მის დამუშავებას დაივიწყებთ, მინიჭება კომპილაციას ვერ გაივლის — უფასო შეხსენება.

default: { const _exhaustive: never = s // ✕ თუ ახალი kind დაუმუშავებელია return _exhaustive }
04 · ჯენერიკები 5 წთ

დაწერეთ ერთხელ და დაე, ტიპი
მიედინოს ბოლომდე.

ჯენერიკი არის ტიპის პარამეტრი — ადგილის დამჭერი, რომელიც <T>-ით იწერება და რომელსაც გამომძახებელი ავსებს. ის ერთ ფუნქციას ან ტიპს ნებისმიერ ტიპზე მუშაობის საშუალებას აძლევს და მაინც ზუსტად ახსოვს, რომელი გამოიყენეს. ალტერნატივა, any, ასევე "მუშაობს" — მაგრამ ამ ცოდნას გადაყრის.

ჯენერიკი — ტიპი პარამეტრით, რომელსაც მოგვიანებით ავსებთ. Array<T> ყოველდღიური მაგალითია: T[] არის "რაღაც ტიპის მასივი", და როგორც კი string[]-ს შექმნით, კომპილატორმა იცის, რომ ყოველი ელემენტი string-ია. აბსტრაქციას ერთხელ წერთ; კონკრეტული ტიპი კი თან მიჰყვება.

გადაეცით string[] და შედეგი string-ია — და არა any. გამომძახებლის მიწოდებული ტიპი შესასვლელიდან გამოსასვლელამდე მიედინება.

სინტაქსის კითხვა

  • <T> სახელის შემდეგ პარამეტრს აცხადებს; T უბრალოდ მიღებული ასოა.
  • T-ს იშვიათად გადასცემთ ცალსახად — TypeScript მას ასკვნის თქვენ მიერ მიცემული არგუმენტებიდან.
  • <T extends …> ზღუდავს, რა შეიძლება იყოს T, ასე რომ გარანტირებულ ველებს უსაფრთხოდ იყენებთ.
  • <T = string> ნაგულისხმევს აძლევს, როცა ვერაფერი დაისკვნება.
// preserves the element type for every caller
function first<T>(arr: T[]): T | undefined {
  return arr[0]
}
const n = first([1, 2, 3])     // number | undefined
const s = first(["a", "b"])    // string | undefined

// a constraint lets you use a guaranteed field
function longest<T extends { length: number }>(a: T, b: T) {
  return a.length >= b.length ? a : b   // .length is safe
}

შეზღუდვა უშვებს ყველაფერს, რასაც length აქვს — სტრიქონები და მასივები შიგნით, უბრალო რიცხვები გარეთ.

any — ტიპს კარგავს
function wrap(x: any): any { return [x] }
const r = wrap(42)   // r is any
r.toExponential()       // no error, no help — could crash
ჯენერიკი — ტიპს ინახავს
function wrap<T>(x: T): T[] { return [x] }
const r = wrap(42)   // r is number[]
r[0].toExponential()    // ✓ checked, with autocomplete

ჯენერიკების წყალობით რჩება Promise<T>, Map<K, V> და თქვენი საყვარელი მონაცემთა სტრუქტურები ტიპებით დაცული. მიმართეთ მას ყოველთვის, როცა ფუნქციის გამომავალი ტიპი დამოკიდებულია მის შემავალ ტიპზე.

05 · utility, მაპირებული და პირობითი ტიპები 5 წთ

ააგეთ ახალი ტიპები ძველებისგან —
ისე, რომ ხელახლა არაფერი აკრიფოთ.

ტიპები კომპოზირდება. ერთი User ფორმიდან შეგიძლიათ გამოიყვანოთ "პატჩი", სადაც ყველა ველი არასავალდებულოა, read-only ასლი ან id-ით დაინდექსებული საძიებო ცხრილი — და ყველა ორიგინალთან სინქრონში დარჩება. TypeScript ამ გარდაქმნების მთელ ინსტრუმენტთა ყუთს გვთავაზობს, და ისინი აგებულია მაპირებული და პირობითი ტიპებისგან, რომლებსაც თავადაც დაწერთ.

utility ტიპები — ჩაშენებული ჯენერიკები, რომლებიც ტიპს გარდაქმნის — მაგალითად Partial<T>, Pick<T, K> და Record<K, V>. თითოეული სინამდვილეში უბრალოდ მაპირებული ტიპია: ციკლი T-ის გასაღებებზე, რომელიც ყოველ თვისებას გადაწერს. გამოიყვანეთ ტიპები მათი დუბლირების ნაცვლად — შეცვალეთ წყარო ერთხელ და ყოველი გამოყვანილი ტიპი მიჰყვება.

Partial გაივლის User-ის ყოველ გასაღებს და ?-ს ამატებს — ერთი გარდაქმნა, ერთგვაროვნად გამოყენებული.

ის, რასაც ყოველკვირეულად გამოიყენებთ

  • Partial<T> / Required<T> — ყოველ ველს არასავალდებულოდ / სავალდებულოდ აქცევს.
  • Pick<T, K> / Omit<T, K> — ტოვებს / ხსნის გასაღებების ქვესიმრავლეს.
  • Record<K, V> — ობიექტი, რომლის გასაღებებია K და მნიშვნელობები V.
  • Readonly<T>, ReturnType<F>, Awaited<P> — და სხვები.
U
utility ტიპები
გამოიყვანეთ მონათესავე ტიპი ახლის წერის ნაცვლად.
+

შეინარჩუნეთ ერთი ჭეშმარიტების წყარო. ფორმა მონახაზს არედაქტირებს, ამიტომ მას ყველა ველი არასავალდებულო სჭირდება; საძიებო ცხრილს კი Record უნდა. გამოიყვანეთ ორივე User-იდან და ისინი ვერასდროს დაშორდებიან ერთმანეთს.

interface User { id: number; name: string; email: string } type Draft = Partial<User> // ყველა არასავალდებულო type Card = Pick<User, "id" | "name"> // ქვესიმრავლე type ById = Record<number, User> // საძიებო რუკა type Public = Omit<User, "email"> // ველის მოხსნა
M
მაპირებული ტიპი
გაიარეთ ტიპის გასაღებები ციკლით და თითოეული გადაწერეთ.
+

მაპირებული ტიპი გასაღებების for-ციკლია: [K in keyof T] ყოველ თვისებას სტუმრობს, რომ მისი მოდიფიკატორი ან ტიპი შეცვალოთ. ზემოთ ჩამოთვლილი ყოველი utility ტიპი სწორედ ასეთია.

საკუთარი Partial
type MyPartial<T> = { [K in keyof T]?: T[K] } // "?" added to every key
read-only შტამპი
type Frozen<T> = { readonly [K in keyof T]: T[K] } // readonly added everywhere
C
პირობითი ტიპები
ტიპი, რომელიც განშტოვდება — და infer-ით ამოღებაც შეუძლია.
+

T extends U ? X : Y ტიპებისთვის if-ია. დააწყვილეთ ის infer-თან, რომ ერთი ტიპიდან მეორე ამოიღოთ — ზუსტად ასე მუშაობს ReturnType და Awaited.

// მასივის ელემენტის ტიპის ამოღება type ElementOf<T> = T extends (infer U)[] ? U : T type A = ElementOf<string[]> // string type B = ElementOf<number> // number (მასივი არაა → თავად ის)

ძლიერია, მაგრამ ადვილი გადასაჭარბებელი. თუ პირობით ტიპს კოლეგა ათ წუთს კითხულობს, უფრო მარტივი, ცალსახად დაწერილი ტიპი ხშირად უკეთესი არჩევანია.

06 · tsconfig და strict რეჟიმი 5 წთ

ჩართეთ strict — და გაარკვიეთ
განსხვავება any, unknown & never შორის.

რამდენად მკაცრად გეწინააღმდეგებათ TypeScript, პარამეტრის საკითხია. strict რეჟიმი იმ შემოწმებების კრებულია, რომლებიც ტიპების სისტემას ნამდვილად სანდოს ხდის — უპირველესად კი null-სა და undefined-ს სერიოზულად აღიქვამს. სამი განსაკუთრებული ტიპი კი წყვეტს, რამდენ უსაფრთხოებას ინარჩუნებთ: any სისტემიდან გადის, unknown უსაფრთხოდ რჩება, never კი ნიშნავს "ვერ მოხდება".

მკაცრი რეჟიმი — "strict": true დროშა tsconfig.json-ში — ერთბაშად რთავს შემოწმებების მთელ ოჯახს, მათ შორის strictNullChecks-ს (null და undefined აღარ არის ჩუმად ყოველი ტიპის ნაწილი) და noImplicitAny-ს (ტიპი, რომელიც ვერ დაისკვნება, შეცდომაა და არა უფასო any). ახალი პროექტები მკაცრი რეჟიმით დაიწყეთ; მოგვიანებით მისი დამატება ბევრად რთულია.

unknown ყველა ტიპზე ზემოთ დგას (და შემოწმებას გაიძულებთ); never ყველას ქვემოთაა; any კი სისტემიდან სულ გარეთ გადის.

ამჯობინეთ unknown any-ს

  • any — ამ მნიშვნელობისთვის შემოწმებას თიშავს. ერთმა any-მ შეიძლება ჩუმად დააინფიციროს ყველაფერი, რასაც შეეხება.
  • unknown — "შეიძლება ნებისმიერი იყოს, ამიტომ ჯერ დაამტკიცე, რა არის." უსაფრთხო დასაშვები ადგილი გარე მონაცემებისთვის.
  • never — მნიშვნელობა, რომელიც ვერ იარსებებს: ფუნქცია, რომელიც ყოველთვის აგდებს შეცდომას, ან ამოწურული იუნიონი.
  • მტკიცებები (as, !) შემმოწმებელს გვერდს უვლის — გამოიყენეთ იშვიათად და გააზრებულად.
// any — the checker stops helping
let a: any = JSON.parse(body)
a.user.name.toUpperCase()   // compiles… may explode

// unknown — you must prove the shape first
let u: unknown = JSON.parse(body)
u.user                      // ✕ 'u' is of type 'unknown'
if (typeof u === "object" && u !== null) {
  // now safely narrow further…
}

any ცუდ მონაცემებს კრახამდე ატარებს; unknown კი მათ კარებთანვე აჩერებს, სანამ არ შეამოწმებთ.

ინსტრუმენტების ჯაჭვი — ვინ ამოწმებს და ვინ მხოლოდ ტრანსპილირებს

სასიცოცხლო, ხშირად გამორჩენილი ფაქტი: ბილდის ინსტრუმენტების უმეტესობა ტიპებს საერთოდ არ ამოწმებს. ისინი მათ სისწრაფისთვის ხსნიან. ტიპების შრე რეალურად მხოლოდ tsc-ს ესმის — ამიტომ ტურბოსწრაფ ბილდსაც კი ცალკე ტიპების შემოწმების ნაბიჯი სჭირდება.

tsc — ოფიციალური ტიპების შემმოწმებელი & გამომცემი

საეტალონო კომპილატორი. აქ ის ერთადერთი ინსტრუმენტია, რომელიც თქვენს ტიპებს რეალურად ამტკიცებს, და ისიც, რომელიც ბიბლიოთეკებისთვის .d.ts დეკლარაციის ფაილებს გამოსცემს.

დადებითი
ავტორიტეტული ტიპების შემოწმება; აგენერირებს იმ .d.ts ტიპებს, რომლებსაც მომხმარებლები ეყრდნობიან.
უარყოფითი
ყველაზე ნელი ვარიანტი დიდ რეპოზიტორიებზე (ამ სხვაობის დასახურად Go-ზე ნატიური პორტი პრევიუშია).
როგორ ავირჩიოთ
თქვენი CI-ის ტიპების კარიბჭე და ბიბლიოთეკების ბილდები — გაუშვით tsc --noEmit მაშინაც კი, როცა JS-ს ბანდლერი აწარმოებს.

esbuild — მხოლოდ ტრანსპილაცია, ძალიან სწრაფი

Go-ზე აგებული ბანდლერი/ტრანსპილერი, რომელიც ტიპებს მილიწამებში ხსნის. ის ტიპებს არასდროს უყურებს — უბრალოდ შლის მათ.

დადებითი
უკიდურესად სწრაფი ბილდები და dev-გაშვება; პაწაწინა კონფიგურაცია.
უარყოფითი
ტიპების შემოწმება საერთოდ არაა — ტიპის შეცდომა პირდაპირ გაივლის.
როგორ ავირჩიოთ
აპლიკაციის ბილდები, სადაც სიჩქარეს მნიშვნელობა აქვს; ყოველთვის დააწყვილეთ ცალკე tsc --noEmit-თან.

swc — Rust-ზე აგებული ტრანსპილერი მრავალი ფრეიმვორკის უკან

Rust-ზე აგებული გარდაქმნა, რომელიც ასაზრდოებს ისეთ ინსტრუმენტებს, როგორიცაა Next.js და სწრაფი ტესტ-რანერები. esbuild-ის მსგავსად, ის ფაილ-ფაილ ტრანსპილირებს და ტიპებს შლის.

დადებითი
სწრაფი და ფრეიმვორკში ჩაშენებული; ხშირად უკვე თქვენთვისაა მიერთებული.
უარყოფითი
მხოლოდ ტრანსპილაცია — ტიპების შემოწმება არაა.
როგორ ავირჩიოთ
როცა თქვენი ფრეიმვორკი მას ისედაც იყენებს; შემოწმების ნახევრისთვის tsc შეინარჩუნეთ.

Babel — TypeScript პრესეტის საშუალებით

@babel/preset-typescript ტიპებს არსებული Babel-ის პაიპლაინის შიგნით ხსნის — მოსახერხებელია, როცა უკვე Babel-ზე ხართ.

დადებითი
ერგება კოდის ბაზებს, რომლებიც უკვე Babel-სა და მის პლაგინებზეა აგებული.
უარყოფითი
შემოწმება არაა, ტრანსპილაცია ფაილ-ფაილ ხდება და TS-ის რამდენიმე შესაძლებლობა (მაგალითად const enum და namespace-ები) არ მუშაობს.
როგორ ავირჩიოთ
მხოლოდ მაშინ, თუ Babel-ში უკვე ბევრი გაქვთ ჩადებული — სხვა შემთხვევაში ამჯობინეთ esbuild/swc.
07 · პატერნები და ხაფანგები — სად ცრუობს ტიპები 4 წთ

ტიპები რანტაიმში ქრება — დაიცავით
ის საზღვრები, საიდანაც მონაცემები შემოდის.

ტიპის აღნიშვნა კომპილატორისთვის მიცემული დაპირებაა და არა რანტაიმის შემოწმება. JSON.parse, fetch, ფორმის სხეულები და as მტკიცებები — ყველა მათგანს შეუძლია მოგცეთ მნიშვნელობა, რომლის ნამდვილი ფორმაც გამოცხადებულ ტიპს არ ემთხვევა. გამოსავალი მარტივია: დაავალიდირეთ ტიპიზების გარეშე მოსული მონაცემები საზღვარზე, შემდეგ კი შიგნით თქვენს ტიპებს ენდეთ.

სად ცრუობს ტიპები — გარე სამყაროსთან ყოველ საზღვარზე. თქვენი კოდის შიგნით კომპილატორი ყველაფერს პატიოსნად ინახავს. მაგრამ მონაცემები, რომლებიც ქსელიდან, მონაცემთა ბაზიდან, URL-იდან ან JSON.parse-იდან შემოდის, any/unknown-ად ჩამოდის; შიშველი as User უბრალოდ ამტკიცებს ფორმას, რომელიც არავის შეუმოწმებია. დაპარსეთ ის და ნუ ივარაუდებთ.

დაავალიდირეთ ერთხელ საზღვარზე; ყველაფერი ამ ხაზის შიგნით უკვე თავის ტიპებს ენდობა.

ორი ყოველდღიური უსაფრთხოების ჩვევა

  • as const — ლიტერალს მის ყველაზე ვიწრო ტიპზე აფიქსირებს ("circle" რჩება "circle"-ად და არა string-ად) — იდეალურია კონფიგურაციისა და იუნიონის ტეგებისთვის.
  • satisfies — ამოწმებს მნიშვნელობას ტიპთან ისე, რომ არ აფართოებს მას, ასე რომ გრჩებათ გარანტიაც და დასკვნილი ზუსტი ტიპიც.
const routes = {
  home: "/", docs: "/docs",
} satisfies Record<string, string>
routes.home   // still exactly "/" — not widened
as — შეუმოწმებელი დაპირება
const res = await fetch(url)
const user = await res.json() as User
// "as" verifies NOTHING — if the API changed,
// user.name is undefined and you crash downstream
parse — გადამოწმებული საზღვარზე
const User = z.object({ id: z.number(), name: z.string() })
const res  = await fetch(url)
const user = User.parse(await res.json())
// throws on bad data; user is typed AND real

რანტაიმის ვალიდაციის ბიბლიოთეკები — დაპარსეთ და ნუ ენდობით

ეს ბიბლიოთეკები საშუალებას გაძლევთ, სქემა ერთხელ დაწეროთ და TypeScript-ის ტიპი მისგან დაასკვნათ — ერთი ჭეშმარიტების წყარო, რომელიც კომპილაციასაც ფარავს და რანტაიმსაც. აირჩიეთ თქვენი პრიორიტეტების მიხედვით.

Zod — ერგონომიული ნაგულისხმევი არჩევანი

მოქნილი სქემის მშენებელი; z.infer სტატიკურ ტიპს გამოჰყავს, ეკოსისტემა კი (ფორმები, RPC, ORM-ები) უზარმაზარია.

დადებითი
შესანიშნავი DX, ტიპების დასკვნა და ყველაზე ფართო ეკოსისტემა და ინტეგრაციები.
უარყოფითი
უფრო დიდი ბანდლი და მეტი რანტაიმის ღირებულება, ვიდრე უფრო მსუბუქ ვარიანტებში.
როგორ ავირჩიოთ
გონივრული ნაგულისხმევი არჩევანი აპლიკაციებისა და API-ების უმეტესობისთვის, თუ ბანდლის ზომა კრიტიკული არაა.

Valibot — მოდულური & პაწაწინა

ვალიდატორები დამოუკიდებელი ფუნქციებია, რომლებსაც pipe-ით აწყობთ, ამიტომ ბანდლერები ყველაფერს, რასაც არ აიმპორტებთ, tree-shaking-ით აცილებს.

დადებითი
ძალიან პატარა ბანდლები — იხდით მხოლოდ იმ ვალიდატორებში, რომლებსაც იყენებთ.
უარყოფითი
უფრო ახალგაზრდა ეკოსისტემა; pipe-ების აწყობა ოდნავ უფრო სიტყვამრავალია.
როგორ ავირჩიოთ
კოდი, სადაც ბანდლის ზომას მნიშვნელობა აქვს — edge-ფუნქციები, კლიენტის მხარე, მობილური ვები.

io-ts — ფუნქციური & პრინციპული

კოდეკები, აგებული fp-ts-ზე; დეკოდირება აბრუნებს Either-ს — ან შეცდომებს, ან ტიპიზებულ მნიშვნელობას — ძლიერი კომპოზიციურობით.

დადებითი
მკაცრი, კარგად კომპოზირებადი კოდეკები, რომლებიც ფუნქციურ კოდის ბაზას ერგება.
უარყოფითი
fp-ts-ის ციცაბო სასწავლი მრუდი; ახალ ვარიანტებზე ნაკლებად აქტიური.
როგორ ავირჩიოთ
გუნდები, რომლებიც უკვე fp-ts-ს იყენებენ და ფუნქციური სიმკაცრე ბოლომდე სურთ.
  • მონაცემები, რომლებიც თქვენს ტიპიზებულ კოდს არასდროს ტოვებს (შიდა ფუნქციური გამოძახებები), რანტაიმის ვალიდაციას არ საჭიროებს — კომპილატორი მას ისედაც ფარავს.
  • პაწაწინა სკრიპტი ან სანდო შიდა დატვირთვა? ხელით დაწერილი ტიპის მცველის ფუნქცია სავსებით საკმარისია — ერთი ფორმისთვის დამოკიდებულებას ნუ დაამატებთ.
  • ვალიდაციის ბიბლიოთეკას ნამდვილ საზღვრებზე მიმართეთ: გარე API-ები, მომხმარებლის შეყვანილი მონაცემები, ვებჰუკები, კონფიგურაციის ფაილები — ყველაფერი, რაც თქვენ არ შეგიქმნიათ.
1მიეცით დასკვნას მუშაობის საშუალება. აღნიშნეთ საზღვრები — ფუნქციების ხელმოწერები, ექსპორტები — დანარჩენი კი TypeScript-ს მიანდეთ.
2დომენი იუნიონებით აღწერეთ. დისკრიმინირებული იუნიონები დავიწროებასთან ერთად შეუძლებელ მდგომარეობებს წარმოუდგენელს ხდის.
3მიმართეთ ჯენერიკებს და არა any-ს. გამომძახებლის ტიპი გამჭოლად ატარეთ; ტიპები utility-ებით გამოიყვანეთ და ნუ გაამრავლებთ.
4იმუშავეთ strict-ით და გაუშვით tsc. მკაცრი რეჟიმი პლუს ტიპების შემოწმების ნამდვილი ნაბიჯი — მარტო ტრანსპილერები არასდროს ამოწმებენ.
5დაავალიდირეთ საზღვრებზე. ტიპები რანტაიმში ქრება; ტიპიზების გარეშე მონაცემები დაპარსეთ, სანამ ენდობით.
ცოდნის შემოწმება

დაგამახსოვრდათ?

ხუთი სწრაფი კითხვა სტრუქტურულ ტიპიზაციაზე, დავიწროებაზე, ჯენერიკებზე, მკაცრ რეჟიმსა და იმაზე, სად ცრუობს ტიპები — მყისიერი უკუკავშირი, ავტორიზაციის გარეშე.

შეაფასეთ ეს დასტა
იყავით პირველი

ნავიგაცია ← → ღილაკებით ან სქროლით · უკან ბიბლიოთეკაში