0Pricing
TypeScript Academy · 课时

泛型接口与类型别名

定义泛型接口和类型别名,添加约束与默认值,并理解各自适合发挥作用的场景。

泛型接口与类型别名 是 CoddyKit 上的免费 TypeScript Academy 课时。 这是第 1 节课,共 3 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 TypeScript Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 TypeScript Academy 课程共包含 3 节课。

简介

目标:使用泛型接口和类型别名创建可复用的结构。您将添加约束和默认值,并了解它们之间的主要差异。

课程概览

目标:使用 coroutineScope 应用结构化并发,理解异常传播,并安全地使用取消工具。

关键概念

  • coroutineScope 会等待所有子协程;失败会取消同级协程。
  • 启动会在作用域中立即失败;异步会在 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));

异步与 await 错误

异步操作的异常会在调用 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处理错误,在 await() 时暴露异步失败,并使用 withTimeout 取消长时间运行的任务。

常见问题解答

「泛型接口与类型别名」课时是免费的吗?

是的 — 「泛型接口与类型别名」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 TypeScript Academy 课程的其余内容,请升级到 CoddyKit PRO。 TypeScript Academy 课程共包含 3 节课。

「泛型接口与类型别名」这节课中我会学到什么?

定义泛型接口和类型别名,添加约束与默认值,并理解各自适合发挥作用的场景。 你通过在浏览器中直接运行的动手代码来练习 TypeScript Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 TypeScript Academy 需要有经验吗?

无需任何先前经验。CoddyKit 上的 TypeScript Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 1 节课,共 3 节。

「泛型接口与类型别名」课时需要多长时间?

大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。

我能在这节 TypeScript Academy 课中编写并运行代码吗?

能。每节 TypeScript Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。

此课程中的所有课时

  1. 泛型接口与类型别名
  2. 条件类型(入门)与联合类型上的分发
  3. 数据模型的可复用模式
← 返回 TypeScript Academy