Tarkibga o'tish

18 β€” tsconfig.json chuqur

⬅️ Oldingi: 17 β€” Modullar va deklaratsiya fayllari (.d.ts) Β· 🏠 README Β· Keyingi: 19 β€” DOM va brauzer TypeScript bilan ➑️

Bu bobda: kompilyatorning xulqini boshqaradigan tsconfig.json faylini ichidan o'rganamiz. Uning uch asosiy bloki (compilerOptions, include, exclude), eng muhim flaglar (strict va u yoqadigan butun oila, target, module, lib, outDir/rootDir, esModuleInterop, skipLibCheck, paths alias, sourceMap, declaration, noEmit), nega aynan strictNullChecks "o'yin o'zgartiruvchi" ekanini ko'ramiz; oxirida har loyiha turi (Node, brauzer, kutubxona) uchun tavsiya etilgan boshlang'ich konfiguratsiya beramiz.


Muammo

2-bobda biz npx tsc --init bilan tsconfig.json yaratgan, ichidan bir nechta sozlamani ajratib olib ishlatgan edik. O'shandan beri 15 bob davomida tiplarni o'rgandik β€” lekin har doim bir savol ortda turardi: "TypeScript bu kodni xato deb belgilashi yoki belgilamasligini aslida kim hal qiladi?"

Javob β€” tsconfig.json. Bitta sozlamani o'zgartirsangiz, bir xil kod bir loyihada toza compile bo'ladi, boshqasida xato beradi. Mana real misol:

function birinchiHarf(matn: string | null): string {
  return matn[0];
}

Bu kod xatomi? Bog'liq. Agar strictNullChecks yoqilgan bo'lsa β€” ha, chunki matn null bo'lishi mumkin, null[0] esa dasturni "yiqitadi". Agar o'sha flag o'chiq bo'lsa β€” TypeScript bu kodni jimgina o'tkazib yuboradi va xato faqat foydalanuvchi null bergan paytda, dastur ishlab turganda chiqadi.

Demak tsconfig.json β€” shunchaki "yo'l-yo'riq fayl" emas: u sizning loyihangiz qanchalik xavfsiz ekanini belgilaydi. Bu bobda har bir muhim "rubilnik"ni β€” nima qilishini, nega kerakligini va qachon o'zgartirishni β€” birma-bir ko'rib chiqamiz. Avval umumiy tuzilishdan boshlaymiz.

tsconfig.json uch blokdan iborat

Loyiha ildizidagi tsconfig.json β€” bu oddiy JSON fayl. Uning yuragida uchta narsa turadi:

{
  "compilerOptions": {
    "target": "es2022",
    "strict": true,
    "outDir": "./dist",
    "rootDir": "./src"
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules", "dist"]
}

Uchta blokning vazifasi sodda:

  • compilerOptions β€” eng katta va eng muhim blok. Kod qanday tekshirilishi (strict...) va qanday JavaScript chiqishi (target, module, outDir...) shu yerda belgilanadi. Bob qolganining ko'p qismi shu blok haqida.
  • include β€” qaysi fayllar compile qilinadi. ["src/**/*"] β€” src papkasi ichidagi (ichkaridagi papkalar bilan birga) hamma .ts fayl. ** belgisi "papkalar ichkarisigacha kir" degani.
  • exclude β€” include tanlaganidan nimani olib tashlash. Odatda node_modules (boshqalarning kodi) va dist (compile natijasi) bu yerda turadi.

tsconfig.json uch asosiy bloki: compilerOptions kompilyator qoidalarini, include qaysi fayllarni, exclude esa qaysilarini chetda qoldirishni belgilaydi

πŸ“Œ include yozmasangiz nima bo'ladi? TypeScript tsconfig.json turgan papkadan boshlab hamma .ts faylni oladi β€” node_modules ichidagisini ham. Aynan shuning uchun excludeda node_modules deyarli doim turadi. Lekin includeni aniq yozish (["src/**/*"]) eng tartibli yo'l: kompilyator faqat sizning kodingiz bilan ishlaydi, tezroq bo'ladi va kutilmagan fayllar tekshiruvga tushmaydi.

πŸ’‘ compilerOptionsdan tashqari yana ikki "umumiy" sozlama bor: files (faqat sanab o'tilgan aniq fayllar β€” kichik loyihada) va extends (boshqa tsconfig'dan meros olish β€” masalan "extends": "@tsconfig/node22/tsconfig.json"). Katta loyihalarda extends orqali tayyor, sinalgan asosni olib, ustiga o'zingiznikini qo'shasiz.

strict β€” eng muhim bitta flag

tsconfig.jsonda yozadigan birinchi va eng muhim qatoringiz shu:

{
  "compilerOptions": {
    "strict": true
  }
}

strict β€” bu bitta sozlama emas, balki butun bir oilaning umumiy rubilnigi. Uni true qilsangiz, quyidagilarning hammasi birvarakayiga yonadi:

  • noImplicitAny β€” tipi yozilmagan, TypeScript ham topa olmaydigan o'zgaruvchi/parametr any bo'lib qolishini ta'qiqlaydi (9-bobdagi "yashirin any").
  • strictNullChecks β€” null va undefinedni alohida, jiddiy tip sifatida ko'radi (pastda batafsil).
  • strictFunctionTypes β€” funksiya tiplarini bir-biriga berishda parametrlarni qattiqroq solishtiradi.
  • strictBindCallApply β€” bind, call, apply argumentlarini tekshiradi.
  • strictPropertyInitialization β€” class maydonlari konstruktor ichida boshlanishini talab qiladi (13-bob).
  • noImplicitThis β€” thisning tipi noaniq (any) bo'lib qolsa, xato beradi.
  • useUnknownInCatchVariables β€” catch (e) dagi eni any emas, unknown qiladi (9-bob β€” xavfsizroq).
  • alwaysStrict β€” chiqarilgan .js faylga "use strict" qo'shadi.

strict: true bitta tugma, lekin u sakkizta qattiq tekshiruvni β€” noImplicitAny, strictNullChecks, strictFunctionTypes va boshqalarni β€” birga yoqadi

πŸ“Œ Nega bittalab emas, hammasini birga? Chunki bu sakkiztasi birgalikda "TypeScript kuchi"ni tashkil qiladi. Bittasini o'chirib qo'yish β€” devordan bitta g'isht olib tashlashga o'xshaydi: tirqish paydo bo'ladi va aynan o'sha tirqishdan xato o'tib ketadi. Shuning uchun zamonaviy maslahat oddiy: doim "strict": true.

πŸ’‘ Eski loyihada birdan strict yoqsangiz yuzlab xato chiqishi mumkin β€” bu 23-bobdagi migratsiya mavzusi. Yangi loyihada esa boshidanoq yoqing: keyin qo'shilgan har bir qator allaqachon "to'g'ri" yoziladi.

Endi strict ichidagi ikki eng muhim a'zoni β€” noImplicitAny va strictNullChecksni β€” alohida, kod bilan ko'ramiz.

noImplicitAny β€” tipsiz parametrga yo'l yo'q

Quyidagi funksiyada son parametrining tipi yozilmagan, TypeScript uni hech qayerdan topa olmaydi:

function ikkilantir(son) {     // ❌ Xato: son uchun tip yo'q
  return son * 2;
}
console.log(ikkilantir(21));

strict (demak noImplicitAny ham) yoqilgan bo'lsa:

error TS7006: Parameter 'son' implicitly has an 'any' type.

Tarjimasi: "son parametri yashirincha any tipiga ega bo'lib qoldi". TypeScript shunday deydi: "men son nimaligini bilmayman, sen ham aytmading β€” bu any, lekin men anyni jimgina qabul qilmayman". Tuzatish β€” tipni ochiq yozish:

function ikkilantir(son: number): number {   // βœ… endi toza
  return son * 2;
}
console.log(ikkilantir(21));

πŸ“Œ Diqqat: bu faqat TypeScript o'zi topa olmaydigan joylarga tegishli. const ismlar = ["Ali", "Vali"] da tipni yozmasangiz ham xato bo'lmaydi β€” chunki TypeScript uni qiymatdan o'qib string[] deb biladi (3-bobdagi inference). noImplicitAny faqat "haqiqatan ham noaniq" qolgan joylarni ushlaydi.

strictNullChecks β€” nega o'yin o'zgartiruvchi

Bu β€” strict oilasidagi eng muhim flag. U yoqilmagan bo'lsa, TypeScript null va undefinedni deyarli har qanday tipga "yashirincha kiritib yuboradi" β€” ya'ni string deb e'lon qilingan o'zgaruvchiga null ham sig'averadi. Bu esa JavaScript'ning eng mashhur xatosiga ("Cannot read property of null") yo'l ochadi.

strictNullChecks yoqilgan bo'lsa, hamma narsa o'zgaradi. Bobning boshidagi muammoga qaytamiz:

function birinchiHarf(matn: string | null): string {
  return matn[0];     // ❌ Xato: matn null bo'lishi mumkin
}
error TS18047: 'matn' is possibly 'null'.

TypeScript aytadi: "matn null bo'lishi mumkin β€” null[0] esa dasturni yiqitadi". Bu β€” bebaho ogohlantirish: xato foydalanuvchigacha emas, hali yozayotganingizda topildi. Tuzatish β€” 8-bobdagi narrowing bilan nullni ajratib olish:

function birinchiHarf(matn: string | null): string {
  if (matn === null) {
    return "(bo'sh)";       // null holatini alohida hal qildik
  }
  return matn[0];           // βœ… bu yerda matn aniq string
}
console.log(birinchiHarf("Toshkent"));
console.log(birinchiHarf(null));

Optional property bilan ham xuddi shu himoya ishlaydi:

interface Foydalanuvchi {
  id: number;
  ism: string;
  telefon?: string;     // bo'lishi shart emas -> string | undefined
}

function telefonKorsat(u: Foydalanuvchi): string {
  if (u.telefon === undefined) {
    return u.ism + ": telefon yo'q";
  }
  return u.ism + ": " + u.telefon;   // βœ… bu yerda telefon aniq string
}

const a: Foydalanuvchi = { id: 1, ism: "Aziz" };
const b: Foydalanuvchi = { id: 2, ism: "Malika", telefon: "+998901234567" };
console.log(telefonKorsat(a));
console.log(telefonKorsat(b));

πŸ“Œ Nega aynan "o'yin o'zgartiruvchi"? Chunki JavaScript dasturlarining juda katta qismi null/undefined bilan bog'liq xatolarda "yiqiladi". strictNullChecks butun bu sinf xatolarni compile vaqtida ushlab beradi. TypeScript yozishning eng katta sababi aynan shu β€” uni o'chirib qo'ysangiz, asbobning yarmini tashlab yuborgan bo'lasiz.

πŸ’‘ Yana bir keng tarqalgan flag bor (lekin strict ichida emas, alohida yoqiladi): noUncheckedIndexedAccess. U ismlar[0] natijasini string emas, string | undefined qiladi β€” chunki massivda bunday indeks bo'lmasligi ham mumkin. Juda foydali, ammo qattiq; uni keyinroq, qulay bo'lganda qo'shasiz.

strictFunctionTypes β€” funksiyalarni qattiqroq solishtirish

Bu flag ham strict ichida. U funksiya tipini boshqa funksiya tipiga berganda parametrlarni qattiq tekshiradi:

type Tekshiruvchi = (x: string | number) => boolean;

const faqatString = (x: string): boolean => x.length > 0;

const t: Tekshiruvchi = faqatString;   // ❌ Xato
error TS2322: Type '(x: string) => boolean' is not assignable to type 'Tekshiruvchi'.
  Types of parameters 'x' and 'x' are incompatible.
    Type 'string | number' is not assignable to type 'string'.

Mantiq: Tekshiruvchi o'rniga turadigan funksiya string ham, number ham qabul qila olishi kerak. faqatString esa faqat string oladi β€” agar kimdir unga number bersa, x.length qatorida yiqiladi. Shuning uchun TypeScript bu almashishni rad etadi. Flag o'chiq bo'lsa, bu xavfli kod jimgina o'tib ketardi.

target β€” qaysi JavaScript versiyasiga compile qilamiz

target TypeScript'ga "men kodni qanchalik zamonaviy JavaScript muhitida ishlataman?" deb aytadi. Misol kodimiz:

const f = async (): Promise<number> => 42;
class Hayvon {
  constructor(public nom: string) {}
}

target: "es2020" bo'lsa, chiqqan .js deyarli o'zgarmaydi β€” async, arrow funksiya va class shundayligicha qoladi, chunki zamonaviy muhit ularni allaqachon tushunadi:

const f = async () => 42;
class Hayvon { ... }

target: "es5" bo'lsa (juda eski brauzerlar uchun), TypeScript bu zamonaviy sintaksisni pasaytiradi ("downlevel"): classni oddiy functionga, asyncni esa katta __awaiter yordamchi blokiga aylantiradi. Natijada kod ko'p, og'ir va o'qish qiyin bo'ladi.

target eski (es5) bo'lsa async/class zamonaviy sintaksisi katta yordamchi kodga pasaytiriladi; yangi (es2020) bo'lsa o'sha sintaksis saqlanadi; module esa import yoki require tanlaydi

πŸ“Œ 2026 holati: TypeScript 6.x da target: "es5" eskirgan (deprecated) deb belgilangan va kelajakda olib tashlanadi. Zamonaviy maslahat β€” kamida es2020, ko'pincha es2022 tanlash. Hozir brauzerlar ham, Node.js ham zamonaviy JavaScript'ni to'liq tushunadi, demak eski es5ga "pasaytirish"ning hojati yo'q β€” u faqat kodni shishiradi.

πŸ’‘ target lib bilan ham bog'liq: targetni o'zgartirsangiz, mos keladigan standart tiplar to'plami (lib) ham o'zgaradi. Masalan target: "es2015" da String.prototype.replaceAll (u keyinroq qo'shilgan) mavjud emas deb hisoblanadi va uni ishlatsangiz xato chiqadi. Buni keyinroq lib bo'limida ko'ramiz.

module β€” import/export qanday yoziladi

module chiqqan JavaScript'da modullar qaysi uslubda yozilishini belgilaydi. Manba kodingiz o'zgarmaydi β€” faqat natija. Bitta import { x } from "./m" qatori:

module: "commonjs" da (eski, an'anaviy Node uslubi):

const m_1 = require("./m");

module: "esnext" da (zamonaviy ESM β€” ECMAScript Modules):

import { x } from "./m";

πŸ“Œ Qaysi birini tanlash kerak? Bu loyiha turiga bog'liq: - Zamonaviy Node.js loyihasi uchun β€” "nodenext" (Node'ning o'zi qaysi uslubni kutishini avtomatik hal qiladi). - Brauzer/bundler (Vite, esbuild, webpack) bilan ishlovchi loyiha uchun β€” "esnext". - Eski Node yoki maxsus muhit uchun β€” "commonjs".

πŸ’‘ module bilan birga ko'pincha moduleResolution ham sozlanadi β€” u TypeScript import "./m" da faylni qanday qidirishini belgilaydi. Zamonaviy qiymatlar: bundler ishlatsangiz "bundler", Node loyihasida "nodenext". Ikkalasi mos kelishi kerak: "module": "nodenext" bilan "moduleResolution": "nodenext" birga yuradi.

outDir va rootDir β€” fayllar qayerga tushadi

Bu juftlik papka tuzilishini tartibga soladi:

  • rootDir β€” manba .ts fayllaringiz qayerda ("./src").
  • outDir β€” compile qilingan .js fayllar qayerga tushishi ("./dist").
{
  "compilerOptions": {
    "rootDir": "./src",
    "outDir": "./dist"
  }
}

src/index.ts β†’ compile β†’ dist/index.js. Manba va natija aralashmaydi: srcda faqat siz yozgan .ts, distda esa faqat mashina chiqargan .js. dist papkasini odatda Git'ga qo'shmaydilar (.gitignorega yoziladi) β€” chunki uni istalgan vaqtda qaytadan yaratsa bo'ladi.

πŸ“Œ rootDir yozmasangiz, TypeScript uni manba fayllaringiz orasidan o'zi taxmin qiladi. Lekin uni aniq yozish xavfsizroq: aks holda noto'g'ri faylni tasodifan qo'shsangiz, butun chiqish tuzilishi siljib ketishi mumkin.

lib β€” qaysi tiplar "mavjud" deb hisoblanadi

lib β€” TypeScript'ga "qaysi tayyor tiplar (standart kutubxonalar) mavjud?" deb aytadi. Masalan, document yoki window (brauzer narsalari) tiplari "dom" kutubxonasida; Promise, Map esa "es2015"da.

{
  "compilerOptions": {
    "target": "es2022",
    "lib": ["es2022", "dom", "dom.iterable"]
  }
}

πŸ“Œ Odatda libni alohida yozmaysiz β€” TypeScript uni targetdan kelib chiqib o'zi tanlaydi (target: "es2022" β†’ es2022 tiplari + default dom). Uni faqat maxsus holatda yozasiz: - Server (Node) loyihasida brauzer tiplari kerak emas β€” "lib": ["es2022"] deb domni olib tashlasangiz, kodda tasodifan documentga murojaat qilsangiz darrov xato chiqadi (server'da document yo'q-ku). - Brauzer loyihasida esa domni qoldirasiz, chunki document, localStorage kabilar kerak.

πŸ’‘ 19-bob to'liq DOM va brauzer tiplariga bag'ishlangan β€” u yerda lib: ["dom"] qanday ishlashini batafsil ko'rasiz.

esModuleInterop β€” eski va yangi modullarni do'st qilish

CommonJS modullari (module.exports = ...) va ES modullari (export default ...) bir-biriga har doim ham silliq ulanmaydi. esModuleInterop: true shu ikkisi orasidagi "import" qoidalarini moslashtiradi β€” natijada eski paketlarni zamonaviy import sintaksisi bilan tabiiy chaqira olasiz:

// esModuleInterop: true bilan bu ishlaydi:
import express from "express";

πŸ“Œ Bu deyarli har bir zamonaviy loyihada true bo'ladi β€” tsc --init ham uni default yoqib beradi. Uni false qilsangiz, ko'p paketlarni g'alati import * as ... shaklida chaqirishga majbur bo'lasiz. Qisqasi: qoldiring true.

skipLibCheck β€” kutubxona tiplarini qayta tekshirmaslik

node_modules ichidagi paketlar o'zlari bilan .d.ts (tip) fayllarini olib keladi. skipLibCheck: true TypeScript'ga "o'sha tashqi .d.ts fayllarni ichidan qayta tekshirma, ishonib o'tib ket" deydi.

{
  "compilerOptions": {
    "skipLibCheck": true
  }
}

πŸ“Œ Nega kerak? Birinchidan, tezlik β€” yuzlab paketning tiplarini har safar tekshirish vaqt oladi. Ikkinchidan, ba'zan ikki paketning tip fayllari o'zaro mos kelmay, sizning kodingizga aloqasi yo'q xatolar chiqaradi. skipLibCheck shu shovqinni o'chiradi. Bu sanoatda deyarli standart β€” true qiling.

πŸ’‘ Muhim farq: skipLibCheck faqat kutubxonalarning .d.ts fayllarini o'tkazib yuboradi. Sizning kodingiz har doim to'liq tekshiriladi β€” demak xavfsiz.

sourceMap va declaration β€” qo'shimcha fayllar

Ikki foydali "qo'shimcha chiqish" sozlamasi:

  • sourceMap: true β€” har .js yoniga .js.map fayl chiqaradi. U brauzer/Node'ga "bu JavaScript qatori aslida qaysi .ts qatoridan kelgan"ini aytadi. Natijada xato bo'lganda brauzer dev-tools sizga asl .ts ni ko'rsatadi, compile qilingan tushunarsiz .js ni emas. Debug uchun bebaho.

  • declaration: true β€” har .ts yoniga .d.ts (tip) fayl chiqaradi. Bu kutubxona yozayotganlar uchun: sizning paketingizdan foydalanadigan boshqa odamlar .js kodni oladi, .d.ts esa ularga tiplaringizni beradi β€” IDE'da avtomatik to'ldirish ishlaydi.

{
  "compilerOptions": {
    "sourceMap": true,
    "declaration": true
  }
}

Tavsiya etilgan konfiguratsiyani compile qilganda dist papkada uchta fayl paydo bo'ladi:

dist/index.js        <- ishga tushadigan JavaScript
dist/index.js.map    <- sourceMap (debug uchun)
dist/index.d.ts      <- tip fayli (declaration)

πŸ“Œ declaration odatda faqat kutubxona/paket loyihalarida true qilinadi. Oddiy ilova (web sayt, server) yozayotgan bo'lsangiz, hech kim sizning ichki tiplaringizni import qilmaydi β€” demak declaration shart emas. sourceMap esa deyarli doim foydali.

noEmit β€” faqat tekshir, fayl chiqarma

2-bobda ko'rgan edik: noEmit: true TypeScript'ga "tekshir, lekin .js umuman yaratma" deydi.

{
  "compilerOptions": {
    "noEmit": true,
    "strict": true
  }
}

πŸ“Œ Bu qachon kerak? Zamonaviy loyihalarda ko'pincha .ts β†’ .js aylantirishni boshqa, tezroq asbob (Vite, esbuild, Babel) bajaradi. Bunday holda tscning yagona vazifasi β€” tip qo'riqchisi bo'lib qolish. Aynan shu yerda noEmit ishlatiladi: tsc butun loyihani tekshiradi, lekin hech narsa chiqarmaydi. Buni odatda package.jsonda skript qilib saqlashadi:

{
  "scripts": {
    "type-check": "tsc --noEmit"
  }
}

πŸ’‘ noEmit bilan outDir/declaration birga turishi ziddiyat emas: noEmit ustun keladi β€” hech narsa chiqmaydi. Lekin chalkashmaslik uchun, faqat tekshiruvga ishlatadigan konfiguratsiyada outDir kabilarni yozmaslik tozaroq.

paths va alias β€” uzun import yo'llaridan qutulish

Katta loyihada import yo'llari uzayib, xunuklashib ketadi:

import { qoshish } from "../../../utils/math";

paths bilan bunga qisqa alias (taxallus) beramiz:

{
  "compilerOptions": {
    "paths": {
      "@utils/*": ["./src/utils/*"]
    }
  }
}

Endi har qayerda, qancha papka chuqurda bo'lsangiz ham, shunchaki:

import { qoshish } from "@utils/math";   // βœ… toza va doim bir xil
console.log(qoshish(2, 3));

@utils/math β†’ TypeScript uni ./src/utils/math deb hal qiladi. Yo'l doim loyiha ildizidan hisoblanadi, "necha papka ortga chiqdim?" deb sanashning hojati qolmaydi.

πŸ“Œ 2026 holati β€” muhim o'zgarish: Eski qo'llanmalarda paths bilan birga baseUrl yozish talab qilinardi. TypeScript 6.x da baseUrl eskirgan (deprecated): uni yozsangiz, "Option 'baseUrl' is deprecated" ogohlantirishi chiqadi. Endi pathsdagi yo'llar tsconfig.json turgan papkaga nisbatan yoziladi β€” yuqoridagi misoldek ["./src/utils/*"] deb. Demak baseUrlni umuman yozmang.

πŸ“Œ Yana bir muhim nuqta: paths faqat TypeScript'ga alias'ni tushuntiradi β€” kompilyator import'ni to'g'ri hal qiladi va tekshiradi. Lekin yakuniy ishga tushiradigan muhit (Node yoki bundler) ham bu alias'ni bilishi kerak. Shuning uchun amalda paths bundler (Vite, webpack) yoki tsx/tsconfig-paths kabi asbob bilan birga ishlatiladi β€” ular ham bir xil alias'ni tushunadigan qilib sozlanadi.

Tavsiya etilgan boshlang'ich konfiguratsiya

Endi hammasini birlashtiramiz. Quyidagi β€” zamonaviy Node.js loyihasi uchun ishonchli boshlang'ich nuqta. Bu konfiguratsiya yuqorida sinab ko'rilgan misol kod bilan toza compile bo'ladi va distga .js, .js.map, .d.ts chiqaradi:

{
  "compilerOptions": {
    "target": "es2022",
    "module": "nodenext",
    "moduleResolution": "nodenext",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,
    "outDir": "./dist",
    "rootDir": "./src",
    "sourceMap": true,
    "declaration": true
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules", "dist"]
}

Tekshiruv:

npx tsc        # toza compile bo'ladi, dist papkada uchta fayl paydo bo'ladi

πŸ“Œ forceConsistentCasingInFileNames β€” bu kichik, lekin foydali qo'shimcha: import "./User" va import "./user" ni har xil deb belgilaydi. Windows/macOS fayl nomida katta-kichik harfni ajratmaydi, Linux esa ajratadi β€” bu flag "menda ishlaydi, serverda ishlamaydi" turidagi xatoning oldini oladi.

Loyiha turiga qarab nima o'zgaradi

Asos bir xil β€” faqat bir-ikki qatorni loyiha turiga moslaysiz:

Brauzer (frontend, bundler bilan):

{
  "compilerOptions": {
    "target": "es2022",
    "module": "esnext",
    "moduleResolution": "bundler",
    "lib": ["es2022", "dom", "dom.iterable"],
    "strict": true,
    "skipLibCheck": true,
    "noEmit": true
  },
  "include": ["src/**/*"]
}
Sababi: dom tiplari kerak (document, window); compile'ni bundler qiladi, shuning uchun tsc faqat tekshiradi (noEmit).

Node.js (server/CLI):

{
  "compilerOptions": {
    "target": "es2022",
    "module": "nodenext",
    "moduleResolution": "nodenext",
    "lib": ["es2022"],
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "outDir": "./dist",
    "rootDir": "./src"
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules", "dist"]
}
Sababi: dom yo'q (serverda document bo'lmaydi); tsc haqiqiy .js chiqaradi.

Kutubxona (boshqalar import qiladigan paket):

{
  "compilerOptions": {
    "target": "es2020",
    "module": "esnext",
    "moduleResolution": "bundler",
    "strict": true,
    "skipLibCheck": true,
    "declaration": true,
    "sourceMap": true,
    "outDir": "./dist",
    "rootDir": "./src"
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules", "dist"]
}
Sababi: declaration: true β€” foydalanuvchilarga tiplaringizni .d.ts shaklida berasiz (21-bob); target biroz pastroq (es2020) β€” ko'proq muhitda ishlasin.

πŸ’‘ Tayyor, sinalgan asoslar ham bor: @tsconfig/node22, @tsconfig/strictest kabi paketlarni extends orqali ulab, ustiga faqat o'zingizning outDir/includeingizni qo'shsangiz, eng yangi tavsiyalarni "tekin" olasiz. Boshlaganda esa yuqoridagi qo'lda yozilgan asoslar to'la yetadi va har qatorning nimaligini bilib turasiz.


Endi tsconfig.json siz uchun sirli fayl emas: uning uch bloki, strict oilasi, target/module chiqishga qanday ta'sir qilishi va har loyiha turi uchun ishonchli boshlang'ich konfiguratsiya β€” hammasi qo'lingizda. Eng muhim xulosa bitta jumlada: "strict": true yozing, qolganini loyiha turiga moslang. Keyingi bobda shu sozlamalardan biri β€” lib: ["dom"] β€” ochadigan dunyoga, brauzer va DOM bilan TypeScript ishlashga o'tamiz. Avval esa quyidagi mashqlarni bajaring: ularning ko'pchiligida bitta sozlamani o'zgartirib, natijani o'z ko'zingiz bilan ko'rasiz β€” bilim aynan shunday mustahkamlanadi.

18-bob mashqlari

  1. Yangi ts-config-mashq papkasi yarating, npm init -y qiling, npm i -D typescript bilan TypeScript o'rnating, so'ng npx tsc --init bilan tsconfig.json yarating. Faylni oching β€” qancha sozlama (ko'pchiligi sharh ostida) borligini ko'ring.
  2. Yaratilgan tsconfig.jsonda compilerOptions, include va exclude bloklarini toping. Agar include/exclude yo'q bo'lsa, ularni o'zingiz qo'shing: "include": ["src/**/*"], "exclude": ["node_modules"].
  3. src papkasini yarating, ichiga index.ts yozing (oddiy console.log("salom")). npx tsc ishlating va .js fayl qaysi papkaga tushganini ko'ring. Endi outDirni "./dist", rootDirni "./src" qiling va qayta compile qilib farqni kuzating.
  4. tsconfig.jsondan "strict": true ni vaqtincha olib tashlang (yoki false qiling). Tipsiz parametrli funksiya yozing: function ikkilantir(son) { return son * 2; }. Xato chiqdimi? Endi strictni qaytaring va qayta tekshiring β€” xato kodini (TS....) yozib oling.
  5. strict yoqilgan holatda function birinchiHarf(matn: string | null): string { return matn[0]; } yozing. Qanday xato chiqdi? Endi strictni o'chiring va xatoning yo'qolishini kuzating β€” bu strictNullChecksning ta'siri.
  6. 5-mashqdagi funksiyani strict yoqilgan holda to'g'rilang: if (matn === null) bilan null holatini alohida hal qiling. Toza compile bo'lganiga ishonch hosil qiling.
  7. interface Foydalanuvchi { id: number; ism: string; telefon?: string } yozing. u.telefon ni to'g'ridan-to'g'ri .toUpperCase() qilib ko'ring β€” strictNullChecks qanday xato beradi? Keyin if (u.telefon) bilan himoyalab tuzating.
  8. tsconfig.jsonda "target": "es5" qiling (ogohlantirish chiqishi mumkin β€” eskirganligi haqida). const f = async () => 42; yozgan faylni compile qiling va chiqqan .js ni oching. Endi targetni "es2020" qilib qayta compile qiling β€” chiqqan .js qanchalik soddalashdi?
  9. 8-mashqdagi .js fayllarni yonma-yon solishtiring: es5 da paydo bo'lgan __awaiter kabi qo'shimcha kod nima uchun kerak bo'ldi? es2020 da nega u yo'q?
  10. "module": "commonjs" qilib, import/export ishlatadigan ikki fayl (m.ts va main.ts) yozing va compile qiling. Chiqqan .js da require va exports ni toping. Endi "module": "esnext" (va "moduleResolution": "bundler") qilib qayta compile qiling β€” natijada import qoldimi?
  11. "esModuleInterop": false qilib ko'ring (agar paket o'rnatgan bo'lsangiz, masalan import x from "..." shaklida). Qanday muammo chiqdi? Keyin true qaytaring. Farqni o'z so'zingiz bilan ayting.
  12. "sourceMap": true qilib compile qiling. dist papkada qanday qo'shimcha fayl paydo bo'ldi? Uning kengaytmasi qanday? Bu fayl nima uchun kerakligini izohlang.
  13. "declaration": true qilib compile qiling. distda qanday .d.ts fayl paydo bo'ldi? Uni oching β€” ichida nima bor, sizning .ts koddan qanday farq qiladi?
  14. "noEmit": true qiling va npx tsc ishlating. Bu safar .js fayl yaratildimi? package.json ning scripts qismiga "type-check": "tsc --noEmit" qo'shing va npm run type-check bilan ishlating.
  15. paths alias sinang: tsconfig.jsonga "paths": { "@utils/*": ["./src/utils/*"] } qo'shing (baseUrl YOZMANG β€” u eskirgan). src/utils/math.ts yarating, uni src/index.ts da import { ... } from "@utils/math" bilan chaqiring va npx tsc --noEmit toza o'tishini tekshiring.
  16. "lib": ["es2022"] qilib (domsiz), index.ts da document.title ga murojaat qiling. Qanday xato chiqdi? Endi libga "dom" qo'shing β€” xato yo'qoldimi? Bu Node vs brauzer farqini qanday tushuntiradi?
  17. "strict" ni o'chirib, o'rniga uning a'zolarini bittalab yozib ko'ring: faqat "noImplicitAny": true. Tipsiz parametr xato beradi, lekin string | nullga matn[0] xato bermaydi (chunki strictNullChecks yoqilmagan). Bu strictning nega "oila" ekanini qanday ko'rsatadi?
  18. "forceConsistentCasingInFileNames": true qiling. import "./User" deb yozing, lekin fayl nomi user.ts bo'lsin. Qanday xato chiqdi? Bu Windows va Linux orasidagi qanday muammoning oldini oladi?
  19. Bobdagi "tavsiya etilgan Node.js konfiguratsiyasi"ni to'liq ko'chirib, tsconfig.jsoningizga qo'ying. src/index.ts ga kichik tiplangan funksiya yozing va npx tsc toza o'tib, distda .js, .js.map, .d.ts paydo bo'lganini tasdiqlang.
  20. Uchta loyiha turi (Node, brauzer, kutubxona) uchun tavsiya konfiguratsiyalarini yonma-yon qo'ying va farqlarini ro'yxat qiling: qaysida lib: ["dom"] bor, qaysida noEmit, qaysida declaration: true, va nega β€” har birini bitta jumla bilan izohlang.