TypeScript / Алдаа зохицуулах TypeScript-д

Алдаа зохицуулах TypeScript-д

Бодит програм бичихэд алдаа заавал гарна — сүлжээ тасарна, хэрэглэгч буруу өгөгдөл оруулна, датабаас хариу өгөхгүй байна. Сайн программист алдааг нуухгүй, харин зохицуулж, хэрэглэгчид ойлгомжтой мэдэгдэл харуулдаг.

TypeScript try/catch-ийн catch (алдаа) дотор алдаа unknown төрөлтэй байдаг — энэ нь хэрэглэгчийг алдаа буруу ашиглахаас хамгаалдаг зөв шийдэл юм.

try/catch болон unknown алдаа

JavaScript-т catch-ийн алдаа any байсан бол TypeScript 4.0-аас эхлэн unknown болсон. Тиймээс алдааг ашиглахын өмнө төрлийг шалгах шаардлагатай:

typescript
// ❌ Хуучин буруу арга — TypeScript 4.0-аас алдаа заана
try {
  throw new Error("Алдаа!");
} catch (алдаа: any) {
  console.log(алдаа.message); // any тул аюултай
}

// ✅ Зөв арга — instanceof шалгалт
async function өгөгдөлАвах(url: string): Promise<string> {
  try {
    const хариу = await fetch(url);

    if (!хариу.ok) {
      throw new Error(`Серверийн алдаа: ${хариу.status}`);
    }

    return await хариу.text();
  } catch (алдаа) {
    // алдаа нь unknown — эхлэж шалгана
    if (алдаа instanceof Error) {
      console.error("Алдааны мэдэгдэл:", алдаа.message);
      console.error("Stack:", алдаа.stack);
    } else {
      console.error("Тодорхойгүй алдаа:", алдаа);
    }
    throw алдаа; // дээш дамжуулна
  }
}

Custom Error класс

Стандарт Error нь зөвхөн message агуулдаг. Томоохон програмд алдааны төрөл, код, нэмэлт мэдээлэл хэрэгтэй болдог — custom error класс үүсгэнэ:

typescript
// Суурь custom error
class АппийнАлдаа extends Error {
  constructor(
    мэдэгдэл: string,
    public readonly код: string,
    public readonly статус: number = 500
  ) {
    super(мэдэгдэл);
    this.name = "АппийнАлдаа";
    // TypeScript-д prototype гинжийг засах шаардлагатай
    Object.setPrototypeOf(this, new.target.prototype);
  }
}

// Тусгай алдааны ангилал
class НэвтрэлтийнАлдаа extends АппийнАлдаа {
  constructor(мэдэгдэл: string = "Нэвтрэх шаардлагатай") {
    super(мэдэгдэл, "НЭВТРЭЛТ_ШААРДЛАГАТАЙ", 401);
    this.name = "НэвтрэлтийнАлдаа";
  }
}

class ОлдоогүйнАлдаа extends АппийнАлдаа {
  constructor(нөөц: string) {
    super(`${нөөц} олдсонгүй`, "ОЛДСОНГҮЙ", 404);
    this.name = "ОлдоогүйнАлдаа";
  }
}

class ШалгалтынАлдаа extends АппийнАлдаа {
  constructor(
    мэдэгдэл: string,
    public readonly талбар: string
  ) {
    super(мэдэгдэл, "ШАЛГАЛТЫН_АЛДАА", 400);
    this.name = "ШалгалтынАлдаа";
  }
}

// Ашиглах
async function хэрэглэгчАвах(id: string) {
  if (!id) {
    throw new ШалгалтынАлдаа("ID хоосон байна", "id");
  }

  const хэрэглэгч = await датабааcаасАвах(id);

  if (!хэрэглэгч) {
    throw new ОлдоогүйнАлдаа("Хэрэглэгч");
  }

  return хэрэглэгч;
}

// Алдааг ялган зохицуулах
try {
  const хэрэглэгч = await хэрэглэгчАвах("");
} catch (алдаа) {
  if (алдаа instanceof НэвтрэлтийнАлдаа) {
    // /login руу чиглүүлэх
    console.log("Нэвтрэх хуудас руу очно");
  } else if (алдаа instanceof ОлдоогүйнАлдаа) {
    // 404 хуудас харуулах
    console.log("Олдсонгүй:", алдаа.message);
  } else if (алдаа instanceof ШалгалтынАлдаа) {
    // Хэрэглэгчид талбарын алдаа харуулах
    console.log(`${алдаа.талбар} талбар буруу: ${алдаа.message}`);
  } else {
    // Мэдэгдээгүй алдаа — лог бичиж, ерөнхий мэдэгдэл харуулах
    console.error("Мэдэгдээгүй алдаа:", алдаа);
  }
}

// TypeScript ашиглаагүй функц — дутуу тодорхойлолт засах
async function датабааcаасАвах(_id: string) {
  return null;
}

Result Pattern — алдааг утга болгон буцаах

Зарим тохиолдолд throw ашиглахын оронд алдааг утга болгон буцаах нь кодыг илүү уншихад хялбар болгодог. Энэ аргыг Rust, Go хэлнүүдэд өргөн хэрэглэдэг:

typescript
// Result төрөл тодорхойлох
type Result<T, E = Error> =
  | { амжилттай: true; утга: T }
  | { амжилттай: false; алдаа: E };

// Helper функцууд
function амжилт<T>(утга: T): Result<T> {
  return { амжилттай: true, утга };
}

function алдаа<E = Error>(алдаа: E): Result<never, E> {
  return { амжилттай: false, алдаа };
}

// Result буцаадаг функц — throw хэрэглэхгүй
async function нэвтрэх(
  и_мэйл: string,
  нууцҮг: string
): Promise<Result<{ токен: string }>> {
  if (!и_мэйл.includes("@")) {
    return алдаа(new ШалгалтынАлдаа("И-мэйл буруу", "и_мэйл"));
  }

  try {
    // Серверт хүсэлт илгээх
    const токен = "жишээ_токен_123";
    return амжилт({ токен });
  } catch (е) {
    return алдаа(е instanceof Error ? е : new Error("Сервертэй холбогдож чадсангүй"));
  }
}

// Ашиглах — try/catch шаардлагагүй
const үр_дүн = await нэвтрэх("хэрэглэгч@жишээ.мн", "нууцүг123");

if (үр_дүн.амжилттай) {
  console.log("Токен:", үр_дүн.утга.токен);
} else {
  console.log("Алдаа:", үр_дүн.алдаа.message);
}

Next.js API route дотор алдаа зохицуулах

typescript
// app/api/progress/route.ts
import { NextRequest, NextResponse } from "next/server";

export async function POST(хүсэлт: NextRequest) {
  try {
    const биет = await хүсэлт.json();

    if (!биет.lessonSlug || !биет.courseSlug) {
      return NextResponse.json(
        { алдаа: "lessonSlug болон courseSlug шаардлагатай" },
        { status: 400 }
      );
    }

    // Supabase-д хадгалах...

    return NextResponse.json({ success: true });
  } catch (алдаа) {
    console.error("Progress хадгалахад алдаа:", алдаа);
    return NextResponse.json(
      { алдаа: "Серверийн алдаа гарлаа" },
      { status: 500 }
    );
  }
}

Дараагийн хичээлд:

TypeScript кодоо Jest + ts-jest ашиглан хэрхэн тест бичиж, алдааг автоматаар илрүүлэх тухай сурна.