0Pricing
TypeScript Academy · บทเรียน

อินเทอร์เฟซและนามแฝงชนิดข้อมูลแบบเจเนอริก

กำหนดอินเทอร์เฟซและนามแฝงชนิดข้อมูลแบบเจเนอริก เพิ่มข้อจำกัดและค่าเริ่มต้น พร้อมทำความเข้าใจว่ารูปแบบใดเหมาะกับงานใด

อินเทอร์เฟซและนามแฝงชนิดข้อมูลแบบเจเนอริก เป็นบทเรียน TypeScript Academy ฟรีบน CoddyKit นี่คือบทเรียนที่ 1 จากทั้งหมด 3 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน TypeScript Academy และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส TypeScript Academy มีบทเรียนทั้งหมด 3 บทเรียน

บทนำ

เป้าหมาย: สร้างรูปทรงที่นำกลับมาใช้ซ้ำได้ด้วยอินเทอร์เฟซเจเนริกและนามแฝงชนิด คุณจะเพิ่มข้อจำกัด ค่าเริ่มต้น และเรียนรู้ความแตกต่างที่สำคัญ

ภาพรวมบทเรียน

เป้าหมาย: ใช้การทำงานพร้อมกันแบบมีโครงสร้างด้วย coroutineScope ทำความเข้าใจการส่งต่อข้อยกเว้น และใช้เครื่องมือการยกเลิกอย่างปลอดภัย

แนวคิดสำคัญ

  • coroutineScope รอให้ลูกทั้งหมดทำงานเสร็จ ความล้มเหลวจะยกเลิกงานพี่น้อง
  • launch ล้มเหลวทันทีภายในขอบเขต ส่วน async จะแสดงข้อผิดพลาดเมื่อเรียกใช้ await()
  • withTimeout ยกเลิกงานเมื่อถึงขีดจำกัดเวลา
  • ห่อขอบเขตด้วย try/catch เพื่อจัดการข้อยกเว้น

อินเทอร์เฟซเจเนริก

อินเทอร์เฟซเจเนริกเก็บพารามิเตอร์ชนิดไว้ ผู้เรียกใช้จะแทนที่ด้วยชนิดจริง เช่น Box<number>

interface Box<T> {
  value: T;
}

const nBox: Box<number> = { value: 42 };
const sBox: Box<string> = { value: "ts" };
console.log(nBox.value, sBox.value);

นามแฝงชนิดเจเนริก

นามแฝงชนิดสามารถเป็นเจเนริกและใช้แสดงออบเจ็กต์ ฟังก์ชัน ยูเนียน หรืออินเตอร์เซกชันได้

type Pair<A, B> = { first: A; second: B };

const p1: Pair<number, string> = { first: 1, second: "one" };
const p2: Pair<boolean, boolean> = { first: true, second: false };
console.log(p1, p2);

พฤติกรรมการรอของขอบเขต

coroutineScope สร้างขอบเขตลูกที่เสร็จสมบูรณ์ก็ต่อเมื่อลูกทั้งหมดทำงานเสร็จ

import kotlinx.coroutines.*

suspend fun doTwoTasks(): Unit = coroutineScope {
    // Both run concurrently and the scope waits for them
    launch { delay(150); println("Task A done") }
    launch { delay(100); println("Task B done") }
    // returning Unit implicitly after children complete
}

fun main() = runBlocking {
    doTwoTasks()
    println("Scope completed")
}

ความล้มเหลวยกเลิกงานพี่น้อง

หากลูกตัวหนึ่งโยนข้อยกเว้น ขอบเขตจะยกเลิกงานพี่น้อง และโยนข้อยกเว้นกลับไปยังผู้เรียกใช้ ให้ใช้ try/catch ที่จุดเรียก

import kotlinx.coroutines.*

suspend fun failingGroup() = coroutineScope {
    val a = launch {
        try {
            repeat(5) { i ->
                println("A $i"); delay(60)
            }
        } finally {
            println("A cancelled")
        }
    }
    launch {
        delay(120)
        throw IllegalStateException("boom") // failure in child
    }
    a.join() // will be cancelled when sibling fails
}

fun main() = runBlocking {
    try {
        failingGroup()
    } catch (e: Exception) {
        println("Caught in caller: ${e.message}")
    }
    println("Caller continues")
}

ข้อจำกัด

เพิ่มข้อจำกัดการขยายเพื่อให้ T เปิดให้ใช้สมาชิกที่จำเป็น เช่น id กับเมธอดของอินเทอร์เฟซ

interface Repository<T extends { id: number }> {
  getById(id: number): T | undefined;
  save(entity: T): void;
}

type User = { id: number; name: string };

const memRepo: Repository<User> = {
  store: [] as User[],
  getById(id) {
    return this.store.find(u => u.id === id);
  },
  save(u) {
    this.store.push(u);
  }
} as unknown as Repository<User>;

memRepo.save({ id: 1, name: "Ada" });
console.log(memRepo.getById(1));

ข้อผิดพลาดของ async และ await

ข้อยกเว้นของ async จะถูกยกขึ้นเมื่อเรียกใช้ await() ให้จัดการด้วย try/catch

import kotlinx.coroutines.*

suspend fun parseAsync(s: String) = coroutineScope {
    val deferred = async {
        delay(50)
        s.toInt() // may throw NumberFormatException
    }
    try {
        println("Result = ${deferred.await()}") // error appears here
    } catch (e: NumberFormatException) {
        println("Handled parse error: ${e::class.simpleName}")
    }
}

fun main() = runBlocking {
    parseAsync("12")
    parseAsync("xx")
}

ค่าเริ่มต้นและการต่อยอด

กำหนดพารามิเตอร์ชนิดเริ่มต้นเพื่อความสะดวก และต่อยอดชนิดด้วยเครื่องมือ เช่น Readonly<Box<T>>

interface ApiResponse<T = unknown> {
  data: T;
  ok: boolean;
}

type ReadonlyBox<T> = Readonly<Box<T>>;

const r1: ApiResponse = { data: "ok", ok: true };      // T defaults to unknown
const r2: ApiResponse<number> = { data: 200, ok: true };
const rb: ReadonlyBox<string> = { value: "fixed" };
// rb.value = "change"; // Error: readonly

การยกเลิกด้วย withTimeout

ใช้ withTimeout เพื่อจำกัดเวลาการทำงาน โดยจะยกเลิกลูกและโยน TimeoutCancellationException

import kotlinx.coroutines.*

suspend fun slowOp(): Int {
    delay(300) // pretend work
    return 42
}

fun main() = runBlocking {
    try {
        val result = withTimeout(150) { slowOp() } // cancels after 150ms
        println("Got $result")
    } catch (e: TimeoutCancellationException) {
        println("Timed out and cancelled")
    }
}

ความแตกต่างด้านการรวม

อินเทอร์เฟซรองรับการรวมการประกาศ ส่วนนามแฝงชนิดไม่สามารถรวมได้ ชื่อที่ซ้ำกันจะเป็นข้อผิดพลาด

interface Settings { theme: string }
interface Settings { lang: string }
// Merged: Settings has { theme: string; lang: string }
const s: Settings = { theme: "dark", lang: "en" };

// type Settings = { foo: string };
// Error: Duplicate identifier 'Settings' for type aliases (no merging)

กฎความล้มเหลวของการทำงานพร้อมกันแบบมีโครงสร้าง

ใน coroutineScope จะเกิดอะไรขึ้นหากคอร์รูทีนลูกตัวหนึ่งล้มเหลวพร้อมข้อยกเว้น

ตรวจสอบอินเทอร์เฟซกับชนิด

ตรวจสอบอย่างรวดเร็ว: ข้อความใดเกี่ยวกับอินเทอร์เฟซเจเนริกและนามแฝงชนิดที่เป็น TRUE

สรุปทบทวน

สรุปทบทวน: ใช้อินเทอร์เฟซเจเนริกและนามแฝงชนิดเพื่อจำลองรูปทรงที่นำกลับมาใช้ซ้ำได้ เพิ่มข้อจำกัดและค่าเริ่มต้น และจำไว้ว่าเฉพาะอินเทอร์เฟซเท่านั้นที่รวมกันได้

สรุปทบทวน

สรุปทบทวน: ใช้ coroutineScope เพื่อจัดกลุ่มงาน ลูกที่ล้มเหลวจะยกเลิกงานพี่น้อง จัดการข้อผิดพลาดด้วย try/catch แสดงความล้มเหลวของ async เมื่อเรียกใช้ await() และยกเลิกงานที่ใช้เวลานานด้วย withTimeout

คำถามที่พบบ่อย

บทเรียน “อินเทอร์เฟซและนามแฝงชนิดข้อมูลแบบเจเนอริก” ฟรีหรือไม่

ใช่ — ข้อความเต็มของ “อินเทอร์เฟซและนามแฝงชนิดข้อมูลแบบเจเนอริก” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส TypeScript Academy ให้อัปเกรดเป็น CoddyKit PRO คอร์ส TypeScript Academy มีบทเรียนทั้งหมด 3 บทเรียน

คุณจะเรียนรู้อะไรในบทเรียน “อินเทอร์เฟซและนามแฝงชนิดข้อมูลแบบเจเนอริก”

กำหนดอินเทอร์เฟซและนามแฝงชนิดข้อมูลแบบเจเนอริก เพิ่มข้อจำกัดและค่าเริ่มต้น พร้อมทำความเข้าใจว่ารูปแบบใดเหมาะกับงานใด คุณปฏิบัติ TypeScript Academy ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน

คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน TypeScript Academy หรือไม่

ไม่จำเป็นต้องมีประสบการณ์มาก่อน TypeScript Academy บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 1 จากทั้งหมด 3 บทเรียน

บทเรียน “อินเทอร์เฟซและนามแฝงชนิดข้อมูลแบบเจเนอริก” ใช้เวลานานแค่ไหน

บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย

ฉันเขียนและรันโค้ดในบทเรียน TypeScript Academy นี้ได้ไหม

ได้ บทเรียน TypeScript Academy ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ

บทเรียนทั้งหมดในหลักสูตรนี้

  1. อินเทอร์เฟซและนามแฝงชนิดข้อมูลแบบเจเนอริก
  2. ชนิดข้อมูลแบบมีเงื่อนไข (เบื้องต้น) และการกระจายบนยูเนียน
  3. รูปแบบที่นำกลับมาใช้ใหม่สำหรับโมเดลข้อมูล
← กลับไปที่ TypeScript Academy