feat: add TypeScript lessons and learning panel

- Introduced a new script to check TypeScript lesson files for errors.
- Created a main TypeScript file to render lessons and their details.
- Added lesson definitions with starter and answer codes.
- Implemented a user interface for navigating and running lessons.
- Styled the application with CSS for a better user experience.
- Updated README to reflect the new TypeScript section and usage instructions.
This commit is contained in:
charlie
2026-03-19 10:06:11 +08:00
parent 69a4ae3178
commit f3bdaa4e88
146 changed files with 5951 additions and 9 deletions

View File

@@ -0,0 +1,21 @@
# 练习 1JavaScript 和 TypeScript 的差别
## 目标
理解 TypeScript 为什么能在写代码时提前发现类型问题。
## 你要练什么
- 参数类型
- 返回值类型
- 类型报错的意义
## 任务
- 观察 `add` 函数的类型标注
- 看懂为什么 `add(1, "2")` 会在 TypeScript 里报错
## 文件
- [starter.ts](/Users/lijiaqing/home/wwwroot/front-end-example/06-typescript/01-js-vs-ts/starter.ts)
- [answer.ts](/Users/lijiaqing/home/wwwroot/front-end-example/06-typescript/01-js-vs-ts/answer.ts)

View File

@@ -0,0 +1,10 @@
function add(a: number, b: number): number {
return a + b;
}
const result = add(1, 2);
console.log(result);
// const wrongResult = add(1, "2");
// TypeScript 会在写代码阶段直接提示:
// 第二个参数应该是 number但这里传入了 string。

View File

@@ -0,0 +1,11 @@
function add(a: number, b: number): number {
return a + b;
}
const result = add(1, 2);
console.log(result);
// 任务:
// 1. 观察 add 的参数和返回值类型
// 2. 尝试把下面这行取消注释,看看 TypeScript 为什么会报错
// const wrongResult = add(1, "2");

View File

@@ -0,0 +1,21 @@
# 练习 2基本类型标注
## 目标
学会给最常见的值加上类型标注。
## 你要练什么
- `number`
- `string`
- `boolean`
## 任务
- 给年龄、姓名、付费状态加类型
- 再输出一段清晰的学习信息
## 文件
- [starter.ts](/Users/lijiaqing/home/wwwroot/front-end-example/06-typescript/02-basic-type-annotations/starter.ts)
- [answer.ts](/Users/lijiaqing/home/wwwroot/front-end-example/06-typescript/02-basic-type-annotations/answer.ts)

View File

@@ -0,0 +1,5 @@
let age: number = 21;
let userName: string = "林晨";
let isPaid: boolean = true;
console.log(`${userName} 今年 ${age} 岁,课程付费状态:${isPaid}`);

View File

@@ -0,0 +1,7 @@
let age: number = 18;
let userName: string = "Tom";
let isPaid: boolean = true;
// 任务:
// 1. 修改上面的值,让它们更像一个学习者资料
// 2. 输出一句完整信息

View File

@@ -0,0 +1,22 @@
# 练习 3数组和函数类型
## 目标
学会给数组和函数写类型。
## 你要练什么
- `number[]`
- 参数类型
- 返回值类型
## 任务
- 定义一个数字数组
- 写一个 `sum` 函数
- 让它接收两个数字并返回数字结果
## 文件
- [starter.ts](/Users/lijiaqing/home/wwwroot/front-end-example/06-typescript/03-array-and-function-types/starter.ts)
- [answer.ts](/Users/lijiaqing/home/wwwroot/front-end-example/06-typescript/03-array-and-function-types/answer.ts)

View File

@@ -0,0 +1,10 @@
const scoreList: number[] = [82, 90, 95];
function sum(a: number, b: number): number {
return a + b;
}
const total = sum(88, 12);
console.log(scoreList);
console.log(total);

View File

@@ -0,0 +1,11 @@
const scoreList: number[] = [82, 90, 95];
function sum(a: number, b: number): number {
// 返回两个数字之和
return 0;
}
// 任务:
// 1. 改写 sum 的返回值
// 2. 调用 sum
// 3. 输出 scoreList 和 sum 结果

View File

@@ -0,0 +1,22 @@
# 练习 4interface 描述对象结构
## 目标
学会用 `interface` 给对象建立结构约束。
## 你要练什么
- `interface`
- 对象结构
- 类型约束
## 任务
- 写一个 `User` 接口
- 用它约束一个学习者对象
- 输出对象中的关键信息
## 文件
- [starter.ts](/Users/lijiaqing/home/wwwroot/front-end-example/06-typescript/04-interface-object-shape/starter.ts)
- [answer.ts](/Users/lijiaqing/home/wwwroot/front-end-example/06-typescript/04-interface-object-shape/answer.ts)

View File

@@ -0,0 +1,14 @@
interface User {
id: number;
name: string;
age: number;
}
const user: User = {
id: 1,
name: "林晨",
age: 21,
};
console.log(user.name);
console.log(user.age);

View File

@@ -0,0 +1,15 @@
interface User {
id: number;
name: string;
age: number;
}
const user: User = {
id: 1,
name: "Tom",
age: 20,
};
// 任务:
// 1. 把对象内容改成学习者资料
// 2. 输出 name 和 age

View File

@@ -0,0 +1,21 @@
# 练习 5泛型函数
## 目标
理解泛型是在保持输入输出类型一致。
## 你要练什么
- 泛型 `<T>`
- 输入输出同类型
## 任务
- 写一个 `getData` 泛型函数
- 传入什么类型,就返回什么类型
- 分别用数字和字符串调用
## 文件
- [starter.ts](/Users/lijiaqing/home/wwwroot/front-end-example/06-typescript/05-generic-functions/starter.ts)
- [answer.ts](/Users/lijiaqing/home/wwwroot/front-end-example/06-typescript/05-generic-functions/answer.ts)

View File

@@ -0,0 +1,9 @@
function getData<T>(data: T): T {
return data;
}
const numberResult = getData<number>(1);
const stringResult = getData<string>("abc");
console.log(numberResult);
console.log(stringResult);

View File

@@ -0,0 +1,9 @@
function getData<T>(data: T): T {
// 返回 data
return data;
}
// 任务:
// 1. 用 number 调用
// 2. 用 string 调用
// 3. 输出结果

View File

@@ -0,0 +1,22 @@
# 练习 6联合类型和可选属性
## 目标
学会处理不固定的数据结构。
## 你要练什么
- 联合类型
- 可选属性
- 接口扩展场景
## 任务
- 定义一个带可选年龄的 `User` 接口
- 声明一个 `id`,它可以是数字或字符串
- 分别创建有年龄和没有年龄的对象
## 文件
- [starter.ts](/Users/lijiaqing/home/wwwroot/front-end-example/06-typescript/06-union-and-optional-props/starter.ts)
- [answer.ts](/Users/lijiaqing/home/wwwroot/front-end-example/06-typescript/06-union-and-optional-props/answer.ts)

View File

@@ -0,0 +1,19 @@
let id: number | string = "user-1";
interface User {
name: string;
age?: number;
}
const userA: User = {
name: "林晨",
age: 21,
};
const userB: User = {
name: "小周",
};
console.log(id);
console.log(userA);
console.log(userB);

View File

@@ -0,0 +1,12 @@
let id: number | string = 1;
interface User {
name: string;
age?: number;
}
// 任务:
// 1. 把 id 改成字符串也试一次
// 2. 创建一个带 age 的 userA
// 3. 创建一个不带 age 的 userB
// 4. 输出它们

View File

@@ -0,0 +1,22 @@
# 练习 7类型安全渲染
## 目标
把接口、数组和函数类型组合起来,处理一组结构化数据。
## 你要练什么
- `interface`
- 数组类型
- 函数返回值类型
## 任务
- 定义课程接口
- 创建课程数组
- 写一个渲染函数,返回字符串数组
## 文件
- [starter.ts](/Users/lijiaqing/home/wwwroot/front-end-example/06-typescript/07-type-safe-renderer/starter.ts)
- [answer.ts](/Users/lijiaqing/home/wwwroot/front-end-example/06-typescript/07-type-safe-renderer/answer.ts)

View File

@@ -0,0 +1,19 @@
interface Course {
title: string;
lessons: number;
finished: boolean;
}
const courses: Course[] = [
{ title: "TypeScript 基础", lessons: 8, finished: true },
{ title: "接口和泛型", lessons: 6, finished: false },
];
function renderCourseLines(list: Course[]): string[] {
return list.map((course) => {
const status = course.finished ? "已完成" : "学习中";
return `${course.title} - ${course.lessons} 节 - ${status}`;
});
}
console.log(renderCourseLines(courses));

View File

@@ -0,0 +1,19 @@
interface Course {
title: string;
lessons: number;
finished: boolean;
}
const courses: Course[] = [
{ title: "TypeScript 基础", lessons: 8, finished: true },
{ title: "接口和泛型", lessons: 6, finished: false },
];
function renderCourseLines(list: Course[]): string[] {
// 返回渲染后的字符串数组
return [];
}
// 任务:
// 1. 实现 renderCourseLines
// 2. 输出结果

View File

@@ -0,0 +1,24 @@
# 练习 8综合小练习
## 目标
把基础类型、接口、泛型和联合类型组合起来,完成一个小型 TypeScript 数据模型。
## 项目名称
类型安全学习摘要
## 任务
请完成一个小程序,要求至少包含:
- 一个 `Student` 接口
- 一个课程数组类型
- 一个联合类型字段
- 一个泛型函数
- 一个格式化输出函数
## 文件
- [starter.ts](/Users/lijiaqing/home/wwwroot/front-end-example/06-typescript/08-final-mini-app/starter.ts)
- [answer.ts](/Users/lijiaqing/home/wwwroot/front-end-example/06-typescript/08-final-mini-app/answer.ts)

View File

@@ -0,0 +1,36 @@
type StudentId = number | string;
interface Course {
title: string;
finished: boolean;
}
interface Student {
id: StudentId;
name: string;
age?: number;
courses: Course[];
}
function pickFirst<T>(list: T[]): T {
return list[0];
}
function formatStudent(student: Student): string {
const courseCount = student.courses.length;
return `${student.name} 当前有 ${courseCount} 门课程,编号是 ${student.id}`;
}
const student: Student = {
id: "stu-1",
name: "林晨",
courses: [
{ title: "基本类型", finished: true },
{ title: "接口", finished: false },
],
};
const firstCourse = pickFirst(student.courses);
console.log(formatStudent(student));
console.log(firstCourse);

View File

@@ -0,0 +1,36 @@
type StudentId = number | string;
interface Course {
title: string;
finished: boolean;
}
interface Student {
id: StudentId;
name: string;
age?: number;
courses: Course[];
}
function pickFirst<T>(list: T[]): T {
return list[0];
}
function formatStudent(student: Student): string {
// 返回一段摘要文字
return "";
}
const student: Student = {
id: "stu-1",
name: "林晨",
courses: [
{ title: "基本类型", finished: true },
{ title: "接口", finished: false },
],
};
// 任务:
// 1. 实现 formatStudent
// 2. 用 pickFirst 取第一门课程
// 3. 输出摘要和第一门课程

170
06-typescript/README.md Normal file
View File

@@ -0,0 +1,170 @@
# TypeScript类型系统
这部分只解决一个问题:你能不能在写代码阶段就让错误尽量提前暴露,而不是把问题留到运行时。
## 学完后你应该掌握
- TypeScript 和 JavaScript 的差别
- 基本类型标注
- 数组类型和函数参数/返回值类型
- `interface` 描述对象结构
- 泛型函数的基本写法
- 联合类型和可选属性
- 如何用类型约束一组业务数据
- 如何把这些类型能力组合成一个小程序
## TypeScript 是什么
TypeScript 不是让程序“更能运行”,而是让程序“更不容易写错”。
它回答的是:
- 这个值应该是什么类型
- 这个函数该接收什么参数
- 这个对象应该有哪些字段
- 一组数据之间的结构是否一致
如果 JavaScript 更关注“能不能表达逻辑”,那么 TypeScript 更关注“表达出来的逻辑有没有类型保障”。
## 必须建立的 5 个核心意识
### 1. 类型信息是写给人和工具看的约束
```ts
function add(a: number, b: number): number {
return a + b;
}
```
这段代码的重点不是语法更复杂,而是把“这个函数只能接收数字”说清楚了。
### 2. TypeScript 的价值主要发生在运行前
TypeScript 很多时候不是修复 bug而是阻止 bug 被写进去。
### 3. 对象结构要先约定,再使用
当数据开始变复杂时,优先考虑用 `interface` 或类型别名描述结构。
### 4. 泛型是在复用“结构能力”
泛型不是高难技巧,它本质上是在说:
- 这个函数可以处理很多类型
- 但输入和输出之间的类型关系要保持一致
### 5. 类型要服务于业务,不要为了类型而类型
目标不是把代码写得最花,而是让代码更清晰、更稳。
## 高频概念速记
### 基本类型
- `number`
- `string`
- `boolean`
### 容器类型
- `number[]`
- `string[]`
- `Array<T>`
### 函数类型
- 参数类型
- 返回值类型
### 对象结构
- `interface`
### 进阶类型
- 泛型 `<T>`
- 联合类型 `|`
- 可选属性 `?`
## 学习顺序
1. JavaScript 和 TypeScript 的差别
2. 基本类型标注
3. 数组和函数类型
4. `interface`
5. 泛型
6. 联合类型和可选属性
7. 类型安全渲染
8. 综合小练习
## 练习目录
- [01-js-vs-ts/README.md](/Users/lijiaqing/home/wwwroot/front-end-example/06-typescript/01-js-vs-ts/README.md)
- [02-basic-type-annotations/README.md](/Users/lijiaqing/home/wwwroot/front-end-example/06-typescript/02-basic-type-annotations/README.md)
- [03-array-and-function-types/README.md](/Users/lijiaqing/home/wwwroot/front-end-example/06-typescript/03-array-and-function-types/README.md)
- [04-interface-object-shape/README.md](/Users/lijiaqing/home/wwwroot/front-end-example/06-typescript/04-interface-object-shape/README.md)
- [05-generic-functions/README.md](/Users/lijiaqing/home/wwwroot/front-end-example/06-typescript/05-generic-functions/README.md)
- [06-union-and-optional-props/README.md](/Users/lijiaqing/home/wwwroot/front-end-example/06-typescript/06-union-and-optional-props/README.md)
- [07-type-safe-renderer/README.md](/Users/lijiaqing/home/wwwroot/front-end-example/06-typescript/07-type-safe-renderer/README.md)
- [08-final-mini-app/README.md](/Users/lijiaqing/home/wwwroot/front-end-example/06-typescript/08-final-mini-app/README.md)
## 过关标准
如果你能独立做到下面这些,就说明这一章已经基本过关:
- 能给基本值加类型标注
- 能给数组和函数写正确类型
- 能用 `interface` 描述对象结构
- 能写一个简单泛型函数
- 能理解联合类型和可选属性的使用场景
- 能看懂 TypeScript 在写代码阶段提示的问题
- 能把类型约束用到一个小型数据模型里
## 学习建议
- 先保证类型写对,再考虑写得更少
- 报错时先看“期望类型”和“实际类型”分别是什么
- 不确定对象结构时,先写接口再写数据
- 遇到泛型不要先背定义,先看输入输出是不是保持同类关系
## 运行调试
这一章现在已经做成了 `Vite + TypeScript` 工程。
它保留了原来的 8 个练习目录,同时新增了一个统一的学习面板,你可以在浏览器里切换章节、查看 `starter.ts`、查看 `answer.ts`,并直接看到示例输出。
### 启动方式
在 [06-typescript](/Users/lijiaqing/home/wwwroot/front-end-example/06-typescript) 目录执行:
```bash
npm install
npm run dev
```
### 常用命令
```bash
npm run dev
npm run build
npm run preview
npm run check
npm run check:app
npm run check:lessons
```
### 工程入口
- 入口页面:[index.html](/Users/lijiaqing/home/wwwroot/front-end-example/06-typescript/index.html)
- 应用入口:[src/main.ts](/Users/lijiaqing/home/wwwroot/front-end-example/06-typescript/src/main.ts)
- 章节数据:[src/lessons.ts](/Users/lijiaqing/home/wwwroot/front-end-example/06-typescript/src/lessons.ts)
- 页面样式:[src/style.css](/Users/lijiaqing/home/wwwroot/front-end-example/06-typescript/src/style.css)
- 包管理配置:[package.json](/Users/lijiaqing/home/wwwroot/front-end-example/06-typescript/package.json)
- TypeScript 配置:[tsconfig.json](/Users/lijiaqing/home/wwwroot/front-end-example/06-typescript/tsconfig.json)
### 说明
- `npm run check` 会先检查 `src/` 下的 Vite 入口代码,再逐个检查每个练习目录里的 `starter.ts``answer.ts`
- `npm run check:app` 只检查 Vite 应用入口
- `npm run check:lessons` 会按文件逐个检查 8 组练习,避免不同练习之间的全局变量互相污染
- 原来的练习目录仍然保留,继续作为题目和答案素材使用

15
06-typescript/index.html Normal file
View File

@@ -0,0 +1,15 @@
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta
name="viewport"
content="width=device-width, initial-scale=1.0"
/>
<title>TypeScript Learning Lab</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>

913
06-typescript/package-lock.json generated Normal file
View File

@@ -0,0 +1,913 @@
{
"name": "06-typescript",
"version": "1.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "06-typescript",
"version": "1.0.0",
"dependencies": {
"pnpm": "^10.32.1"
},
"devDependencies": {
"typescript": "latest",
"vite": "latest"
}
},
"node_modules/@emnapi/core": {
"version": "1.9.0",
"resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.9.0.tgz",
"integrity": "sha512-0DQ98G9ZQZOxfUcQn1waV2yS8aWdZ6kJMbYCJB3oUBecjWYO1fqJ+a1DRfPF3O5JEkwqwP1A9QEN/9mYm2Yd0w==",
"dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
"@emnapi/wasi-threads": "1.2.0",
"tslib": "^2.4.0"
}
},
"node_modules/@emnapi/runtime": {
"version": "1.9.0",
"resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.9.0.tgz",
"integrity": "sha512-QN75eB0IH2ywSpRpNddCRfQIhmJYBCJ1x5Lb3IscKAL8bMnVAKnRg8dCoXbHzVLLH7P38N2Z3mtulB7W0J0FKw==",
"dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
"tslib": "^2.4.0"
}
},
"node_modules/@emnapi/wasi-threads": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.0.tgz",
"integrity": "sha512-N10dEJNSsUx41Z6pZsXU8FjPjpBEplgH24sfkmITrBED1/U2Esum9F3lfLrMjKHHjmi557zQn7kR9R+XWXu5Rg==",
"dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
"tslib": "^2.4.0"
}
},
"node_modules/@napi-rs/wasm-runtime": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.1.tgz",
"integrity": "sha512-p64ah1M1ld8xjWv3qbvFwHiFVWrq1yFvV4f7w+mzaqiR4IlSgkqhcRdHwsGgomwzBH51sRY4NEowLxnaBjcW/A==",
"dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
"@emnapi/core": "^1.7.1",
"@emnapi/runtime": "^1.7.1",
"@tybys/wasm-util": "^0.10.1"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/Brooooooklyn"
}
},
"node_modules/@oxc-project/runtime": {
"version": "0.115.0",
"resolved": "https://registry.npmjs.org/@oxc-project/runtime/-/runtime-0.115.0.tgz",
"integrity": "sha512-Rg8Wlt5dCbXhQnsXPrkOjL1DTSvXLgb2R/KYfnf1/K+R0k6UMLEmbQXPM+kwrWqSmWA2t0B1EtHy2/3zikQpvQ==",
"dev": true,
"license": "MIT",
"engines": {
"node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@oxc-project/types": {
"version": "0.115.0",
"resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.115.0.tgz",
"integrity": "sha512-4n91DKnebUS4yjUHl2g3/b2T+IUdCfmoZGhmwsovZCDaJSs+QkVAM+0AqqTxHSsHfeiMuueT75cZaZcT/m0pSw==",
"dev": true,
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/Boshen"
}
},
"node_modules/@rolldown/binding-android-arm64": {
"version": "1.0.0-rc.9",
"resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-rc.9.tgz",
"integrity": "sha512-lcJL0bN5hpgJfSIz/8PIf02irmyL43P+j1pTCfbD1DbLkmGRuFIA4DD3B3ZOvGqG0XiVvRznbKtN0COQVaKUTg==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"android"
],
"engines": {
"node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@rolldown/binding-darwin-arm64": {
"version": "1.0.0-rc.9",
"resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.0-rc.9.tgz",
"integrity": "sha512-J7Zk3kLYFsLtuH6U+F4pS2sYVzac0qkjcO5QxHS7OS7yZu2LRs+IXo+uvJ/mvpyUljDJ3LROZPoQfgBIpCMhdQ==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@rolldown/binding-darwin-x64": {
"version": "1.0.0-rc.9",
"resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.0-rc.9.tgz",
"integrity": "sha512-iwtmmghy8nhfRGeNAIltcNXzD0QMNaaA5U/NyZc1Ia4bxrzFByNMDoppoC+hl7cDiUq5/1CnFthpT9n+UtfFyg==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@rolldown/binding-freebsd-x64": {
"version": "1.0.0-rc.9",
"resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.0-rc.9.tgz",
"integrity": "sha512-DLFYI78SCiZr5VvdEplsVC2Vx53lnA4/Ga5C65iyldMVaErr86aiqCoNBLl92PXPfDtUYjUh+xFFor40ueNs4Q==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"freebsd"
],
"engines": {
"node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@rolldown/binding-linux-arm-gnueabihf": {
"version": "1.0.0-rc.9",
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.0-rc.9.tgz",
"integrity": "sha512-CsjTmTwd0Hri6iTw/DRMK7kOZ7FwAkrO4h8YWKoX/kcj833e4coqo2wzIFywtch/8Eb5enQ/lwLM7w6JX1W5RQ==",
"cpu": [
"arm"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@rolldown/binding-linux-arm64-gnu": {
"version": "1.0.0-rc.9",
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.0-rc.9.tgz",
"integrity": "sha512-2x9O2JbSPxpxMDhP9Z74mahAStibTlrBMW0520+epJH5sac7/LwZW5Bmg/E6CXuEF53JJFW509uP+lSedaUNxg==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@rolldown/binding-linux-arm64-musl": {
"version": "1.0.0-rc.9",
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.0-rc.9.tgz",
"integrity": "sha512-JA1QRW31ogheAIRhIg9tjMfsYbglXXYGNPLdPEYrwFxdbkQCAzvpSCSHCDWNl4hTtrol8WeboCSEpjdZK8qrCg==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@rolldown/binding-linux-ppc64-gnu": {
"version": "1.0.0-rc.9",
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.0-rc.9.tgz",
"integrity": "sha512-aOKU9dJheda8Kj8Y3w9gnt9QFOO+qKPAl8SWd7JPHP+Cu0EuDAE5wokQubLzIDQWg2myXq2XhTpOVS07qqvT+w==",
"cpu": [
"ppc64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@rolldown/binding-linux-s390x-gnu": {
"version": "1.0.0-rc.9",
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.0-rc.9.tgz",
"integrity": "sha512-OalO94fqj7IWRn3VdXWty75jC5dk4C197AWEuMhIpvVv2lw9fiPhud0+bW2ctCxb3YoBZor71QHbY+9/WToadA==",
"cpu": [
"s390x"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@rolldown/binding-linux-x64-gnu": {
"version": "1.0.0-rc.9",
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.0-rc.9.tgz",
"integrity": "sha512-cVEl1vZtBsBZna3YMjGXNvnYYrOJ7RzuWvZU0ffvJUexWkukMaDuGhUXn0rjnV0ptzGVkvc+vW9Yqy6h8YX4pg==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@rolldown/binding-linux-x64-musl": {
"version": "1.0.0-rc.9",
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.0-rc.9.tgz",
"integrity": "sha512-UzYnKCIIc4heAKgI4PZ3dfBGUZefGCJ1TPDuLHoCzgrMYPb5Rv6TLFuYtyM4rWyHM7hymNdsg5ik2C+UD9VDbA==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@rolldown/binding-openharmony-arm64": {
"version": "1.0.0-rc.9",
"resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.0-rc.9.tgz",
"integrity": "sha512-+6zoiF+RRyf5cdlFQP7nm58mq7+/2PFaY2DNQeD4B87N36JzfF/l9mdBkkmTvSYcYPE8tMh/o3cRlsx1ldLfog==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"openharmony"
],
"engines": {
"node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@rolldown/binding-wasm32-wasi": {
"version": "1.0.0-rc.9",
"resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.0-rc.9.tgz",
"integrity": "sha512-rgFN6sA/dyebil3YTlL2evvi/M+ivhfnyxec7AccTpRPccno/rPoNlqybEZQBkcbZu8Hy+eqNJCqfBR8P7Pg8g==",
"cpu": [
"wasm32"
],
"dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
"@napi-rs/wasm-runtime": "^1.1.1"
},
"engines": {
"node": ">=14.0.0"
}
},
"node_modules/@rolldown/binding-win32-arm64-msvc": {
"version": "1.0.0-rc.9",
"resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0-rc.9.tgz",
"integrity": "sha512-lHVNUG/8nlF1IQk1C0Ci574qKYyty2goMiPlRqkC5R+3LkXDkL5Dhx8ytbxq35m+pkHVIvIxviD+TWLdfeuadA==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@rolldown/binding-win32-x64-msvc": {
"version": "1.0.0-rc.9",
"resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.0-rc.9.tgz",
"integrity": "sha512-G0oA4+w1iY5AGi5HcDTxWsoxF509hrFIPB2rduV5aDqS9FtDg1CAfa7V34qImbjfhIcA8C+RekocJZA96EarwQ==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@rolldown/pluginutils": {
"version": "1.0.0-rc.9",
"resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.9.tgz",
"integrity": "sha512-w6oiRWgEBl04QkFZgmW+jnU1EC9b57Oihi2ot3HNWIQRqgHp5PnYDia5iZ5FF7rpa4EQdiqMDXjlqKGXBhsoXw==",
"dev": true,
"license": "MIT"
},
"node_modules/@tybys/wasm-util": {
"version": "0.10.1",
"resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz",
"integrity": "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==",
"dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
"tslib": "^2.4.0"
}
},
"node_modules/detect-libc": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz",
"integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==",
"dev": true,
"license": "Apache-2.0",
"engines": {
"node": ">=8"
}
},
"node_modules/fdir": {
"version": "6.5.0",
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
"integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=12.0.0"
},
"peerDependencies": {
"picomatch": "^3 || ^4"
},
"peerDependenciesMeta": {
"picomatch": {
"optional": true
}
}
},
"node_modules/fsevents": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
}
},
"node_modules/lightningcss": {
"version": "1.32.0",
"resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz",
"integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==",
"dev": true,
"license": "MPL-2.0",
"dependencies": {
"detect-libc": "^2.0.3"
},
"engines": {
"node": ">= 12.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
},
"optionalDependencies": {
"lightningcss-android-arm64": "1.32.0",
"lightningcss-darwin-arm64": "1.32.0",
"lightningcss-darwin-x64": "1.32.0",
"lightningcss-freebsd-x64": "1.32.0",
"lightningcss-linux-arm-gnueabihf": "1.32.0",
"lightningcss-linux-arm64-gnu": "1.32.0",
"lightningcss-linux-arm64-musl": "1.32.0",
"lightningcss-linux-x64-gnu": "1.32.0",
"lightningcss-linux-x64-musl": "1.32.0",
"lightningcss-win32-arm64-msvc": "1.32.0",
"lightningcss-win32-x64-msvc": "1.32.0"
}
},
"node_modules/lightningcss-android-arm64": {
"version": "1.32.0",
"resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz",
"integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MPL-2.0",
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">= 12.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/lightningcss-darwin-arm64": {
"version": "1.32.0",
"resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz",
"integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MPL-2.0",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">= 12.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/lightningcss-darwin-x64": {
"version": "1.32.0",
"resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz",
"integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==",
"cpu": [
"x64"
],
"dev": true,
"license": "MPL-2.0",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">= 12.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/lightningcss-freebsd-x64": {
"version": "1.32.0",
"resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz",
"integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==",
"cpu": [
"x64"
],
"dev": true,
"license": "MPL-2.0",
"optional": true,
"os": [
"freebsd"
],
"engines": {
"node": ">= 12.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/lightningcss-linux-arm-gnueabihf": {
"version": "1.32.0",
"resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz",
"integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==",
"cpu": [
"arm"
],
"dev": true,
"license": "MPL-2.0",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 12.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/lightningcss-linux-arm64-gnu": {
"version": "1.32.0",
"resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz",
"integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MPL-2.0",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 12.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/lightningcss-linux-arm64-musl": {
"version": "1.32.0",
"resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz",
"integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MPL-2.0",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 12.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/lightningcss-linux-x64-gnu": {
"version": "1.32.0",
"resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz",
"integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==",
"cpu": [
"x64"
],
"dev": true,
"license": "MPL-2.0",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 12.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/lightningcss-linux-x64-musl": {
"version": "1.32.0",
"resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz",
"integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==",
"cpu": [
"x64"
],
"dev": true,
"license": "MPL-2.0",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 12.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/lightningcss-win32-arm64-msvc": {
"version": "1.32.0",
"resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz",
"integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MPL-2.0",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">= 12.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/lightningcss-win32-x64-msvc": {
"version": "1.32.0",
"resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz",
"integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==",
"cpu": [
"x64"
],
"dev": true,
"license": "MPL-2.0",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">= 12.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/nanoid": {
"version": "3.3.11",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
"integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
"dev": true,
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/ai"
}
],
"license": "MIT",
"bin": {
"nanoid": "bin/nanoid.cjs"
},
"engines": {
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
}
},
"node_modules/picocolors": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
"dev": true,
"license": "ISC"
},
"node_modules/picomatch": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/jonschlinkert"
}
},
"node_modules/pnpm": {
"version": "10.32.1",
"resolved": "https://registry.npmjs.org/pnpm/-/pnpm-10.32.1.tgz",
"integrity": "sha512-pwaTjw6JrBRWtlY+q07fHR+vM2jRGR/FxZeQ6W3JGORFarLmfWE94QQ9LoyB+HMD5rQNT/7KnfFe8a1Wc0jyvg==",
"license": "MIT",
"bin": {
"pnpm": "bin/pnpm.cjs",
"pnpx": "bin/pnpx.cjs"
},
"engines": {
"node": ">=18.12"
},
"funding": {
"url": "https://opencollective.com/pnpm"
}
},
"node_modules/postcss": {
"version": "8.5.8",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.8.tgz",
"integrity": "sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==",
"dev": true,
"funding": [
{
"type": "opencollective",
"url": "https://opencollective.com/postcss/"
},
{
"type": "tidelift",
"url": "https://tidelift.com/funding/github/npm/postcss"
},
{
"type": "github",
"url": "https://github.com/sponsors/ai"
}
],
"license": "MIT",
"dependencies": {
"nanoid": "^3.3.11",
"picocolors": "^1.1.1",
"source-map-js": "^1.2.1"
},
"engines": {
"node": "^10 || ^12 || >=14"
}
},
"node_modules/rolldown": {
"version": "1.0.0-rc.9",
"resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-rc.9.tgz",
"integrity": "sha512-9EbgWge7ZH+yqb4d2EnELAntgPTWbfL8ajiTW+SyhJEC4qhBbkCKbqFV4Ge4zmu5ziQuVbWxb/XwLZ+RIO7E8Q==",
"dev": true,
"license": "MIT",
"dependencies": {
"@oxc-project/types": "=0.115.0",
"@rolldown/pluginutils": "1.0.0-rc.9"
},
"bin": {
"rolldown": "bin/cli.mjs"
},
"engines": {
"node": "^20.19.0 || >=22.12.0"
},
"optionalDependencies": {
"@rolldown/binding-android-arm64": "1.0.0-rc.9",
"@rolldown/binding-darwin-arm64": "1.0.0-rc.9",
"@rolldown/binding-darwin-x64": "1.0.0-rc.9",
"@rolldown/binding-freebsd-x64": "1.0.0-rc.9",
"@rolldown/binding-linux-arm-gnueabihf": "1.0.0-rc.9",
"@rolldown/binding-linux-arm64-gnu": "1.0.0-rc.9",
"@rolldown/binding-linux-arm64-musl": "1.0.0-rc.9",
"@rolldown/binding-linux-ppc64-gnu": "1.0.0-rc.9",
"@rolldown/binding-linux-s390x-gnu": "1.0.0-rc.9",
"@rolldown/binding-linux-x64-gnu": "1.0.0-rc.9",
"@rolldown/binding-linux-x64-musl": "1.0.0-rc.9",
"@rolldown/binding-openharmony-arm64": "1.0.0-rc.9",
"@rolldown/binding-wasm32-wasi": "1.0.0-rc.9",
"@rolldown/binding-win32-arm64-msvc": "1.0.0-rc.9",
"@rolldown/binding-win32-x64-msvc": "1.0.0-rc.9"
}
},
"node_modules/source-map-js": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
"integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
"dev": true,
"license": "BSD-3-Clause",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/tinyglobby": {
"version": "0.2.15",
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz",
"integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"fdir": "^6.5.0",
"picomatch": "^4.0.3"
},
"engines": {
"node": ">=12.0.0"
},
"funding": {
"url": "https://github.com/sponsors/SuperchupuDev"
}
},
"node_modules/tslib": {
"version": "2.8.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
"dev": true,
"license": "0BSD",
"optional": true
},
"node_modules/typescript": {
"version": "5.9.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
"dev": true,
"license": "Apache-2.0",
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
},
"engines": {
"node": ">=14.17"
}
},
"node_modules/vite": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/vite/-/vite-8.0.0.tgz",
"integrity": "sha512-fPGaRNj9Zytaf8LEiBhY7Z6ijnFKdzU/+mL8EFBaKr7Vw1/FWcTBAMW0wLPJAGMPX38ZPVCVgLceWiEqeoqL2Q==",
"dev": true,
"license": "MIT",
"dependencies": {
"@oxc-project/runtime": "0.115.0",
"lightningcss": "^1.32.0",
"picomatch": "^4.0.3",
"postcss": "^8.5.8",
"rolldown": "1.0.0-rc.9",
"tinyglobby": "^0.2.15"
},
"bin": {
"vite": "bin/vite.js"
},
"engines": {
"node": "^20.19.0 || >=22.12.0"
},
"funding": {
"url": "https://github.com/vitejs/vite?sponsor=1"
},
"optionalDependencies": {
"fsevents": "~2.3.3"
},
"peerDependencies": {
"@types/node": "^20.19.0 || >=22.12.0",
"@vitejs/devtools": "^0.0.0-alpha.31",
"esbuild": "^0.27.0",
"jiti": ">=1.21.0",
"less": "^4.0.0",
"sass": "^1.70.0",
"sass-embedded": "^1.70.0",
"stylus": ">=0.54.8",
"sugarss": "^5.0.0",
"terser": "^5.16.0",
"tsx": "^4.8.1",
"yaml": "^2.4.2"
},
"peerDependenciesMeta": {
"@types/node": {
"optional": true
},
"@vitejs/devtools": {
"optional": true
},
"esbuild": {
"optional": true
},
"jiti": {
"optional": true
},
"less": {
"optional": true
},
"sass": {
"optional": true
},
"sass-embedded": {
"optional": true
},
"stylus": {
"optional": true
},
"sugarss": {
"optional": true
},
"terser": {
"optional": true
},
"tsx": {
"optional": true
},
"yaml": {
"optional": true
}
}
}
}
}

View File

@@ -0,0 +1,21 @@
{
"name": "06-typescript",
"private": true,
"version": "1.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview",
"check": "tsc --noEmit -p ./tsconfig.json && node ./scripts/check-lessons.mjs",
"check:app": "tsc --noEmit -p ./tsconfig.json",
"check:lessons": "node ./scripts/check-lessons.mjs"
},
"devDependencies": {
"typescript": "latest",
"vite": "latest"
},
"dependencies": {
"pnpm": "^10.32.1"
}
}

518
06-typescript/pnpm-lock.yaml generated Normal file
View File

@@ -0,0 +1,518 @@
lockfileVersion: '9.0'
settings:
autoInstallPeers: true
excludeLinksFromLockfile: false
importers:
.:
dependencies:
pnpm:
specifier: ^10.32.1
version: 10.32.1
devDependencies:
typescript:
specifier: latest
version: 5.9.3
vite:
specifier: latest
version: 8.0.0
packages:
'@emnapi/core@1.9.0':
resolution: {integrity: sha512-0DQ98G9ZQZOxfUcQn1waV2yS8aWdZ6kJMbYCJB3oUBecjWYO1fqJ+a1DRfPF3O5JEkwqwP1A9QEN/9mYm2Yd0w==}
'@emnapi/runtime@1.9.0':
resolution: {integrity: sha512-QN75eB0IH2ywSpRpNddCRfQIhmJYBCJ1x5Lb3IscKAL8bMnVAKnRg8dCoXbHzVLLH7P38N2Z3mtulB7W0J0FKw==}
'@emnapi/wasi-threads@1.2.0':
resolution: {integrity: sha512-N10dEJNSsUx41Z6pZsXU8FjPjpBEplgH24sfkmITrBED1/U2Esum9F3lfLrMjKHHjmi557zQn7kR9R+XWXu5Rg==}
'@napi-rs/wasm-runtime@1.1.1':
resolution: {integrity: sha512-p64ah1M1ld8xjWv3qbvFwHiFVWrq1yFvV4f7w+mzaqiR4IlSgkqhcRdHwsGgomwzBH51sRY4NEowLxnaBjcW/A==}
'@oxc-project/runtime@0.115.0':
resolution: {integrity: sha512-Rg8Wlt5dCbXhQnsXPrkOjL1DTSvXLgb2R/KYfnf1/K+R0k6UMLEmbQXPM+kwrWqSmWA2t0B1EtHy2/3zikQpvQ==}
engines: {node: ^20.19.0 || >=22.12.0}
'@oxc-project/types@0.115.0':
resolution: {integrity: sha512-4n91DKnebUS4yjUHl2g3/b2T+IUdCfmoZGhmwsovZCDaJSs+QkVAM+0AqqTxHSsHfeiMuueT75cZaZcT/m0pSw==}
'@rolldown/binding-android-arm64@1.0.0-rc.9':
resolution: {integrity: sha512-lcJL0bN5hpgJfSIz/8PIf02irmyL43P+j1pTCfbD1DbLkmGRuFIA4DD3B3ZOvGqG0XiVvRznbKtN0COQVaKUTg==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64]
os: [android]
'@rolldown/binding-darwin-arm64@1.0.0-rc.9':
resolution: {integrity: sha512-J7Zk3kLYFsLtuH6U+F4pS2sYVzac0qkjcO5QxHS7OS7yZu2LRs+IXo+uvJ/mvpyUljDJ3LROZPoQfgBIpCMhdQ==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64]
os: [darwin]
'@rolldown/binding-darwin-x64@1.0.0-rc.9':
resolution: {integrity: sha512-iwtmmghy8nhfRGeNAIltcNXzD0QMNaaA5U/NyZc1Ia4bxrzFByNMDoppoC+hl7cDiUq5/1CnFthpT9n+UtfFyg==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [x64]
os: [darwin]
'@rolldown/binding-freebsd-x64@1.0.0-rc.9':
resolution: {integrity: sha512-DLFYI78SCiZr5VvdEplsVC2Vx53lnA4/Ga5C65iyldMVaErr86aiqCoNBLl92PXPfDtUYjUh+xFFor40ueNs4Q==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [x64]
os: [freebsd]
'@rolldown/binding-linux-arm-gnueabihf@1.0.0-rc.9':
resolution: {integrity: sha512-CsjTmTwd0Hri6iTw/DRMK7kOZ7FwAkrO4h8YWKoX/kcj833e4coqo2wzIFywtch/8Eb5enQ/lwLM7w6JX1W5RQ==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm]
os: [linux]
'@rolldown/binding-linux-arm64-gnu@1.0.0-rc.9':
resolution: {integrity: sha512-2x9O2JbSPxpxMDhP9Z74mahAStibTlrBMW0520+epJH5sac7/LwZW5Bmg/E6CXuEF53JJFW509uP+lSedaUNxg==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64]
os: [linux]
libc: [glibc]
'@rolldown/binding-linux-arm64-musl@1.0.0-rc.9':
resolution: {integrity: sha512-JA1QRW31ogheAIRhIg9tjMfsYbglXXYGNPLdPEYrwFxdbkQCAzvpSCSHCDWNl4hTtrol8WeboCSEpjdZK8qrCg==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64]
os: [linux]
libc: [musl]
'@rolldown/binding-linux-ppc64-gnu@1.0.0-rc.9':
resolution: {integrity: sha512-aOKU9dJheda8Kj8Y3w9gnt9QFOO+qKPAl8SWd7JPHP+Cu0EuDAE5wokQubLzIDQWg2myXq2XhTpOVS07qqvT+w==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [ppc64]
os: [linux]
libc: [glibc]
'@rolldown/binding-linux-s390x-gnu@1.0.0-rc.9':
resolution: {integrity: sha512-OalO94fqj7IWRn3VdXWty75jC5dk4C197AWEuMhIpvVv2lw9fiPhud0+bW2ctCxb3YoBZor71QHbY+9/WToadA==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [s390x]
os: [linux]
libc: [glibc]
'@rolldown/binding-linux-x64-gnu@1.0.0-rc.9':
resolution: {integrity: sha512-cVEl1vZtBsBZna3YMjGXNvnYYrOJ7RzuWvZU0ffvJUexWkukMaDuGhUXn0rjnV0ptzGVkvc+vW9Yqy6h8YX4pg==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [x64]
os: [linux]
libc: [glibc]
'@rolldown/binding-linux-x64-musl@1.0.0-rc.9':
resolution: {integrity: sha512-UzYnKCIIc4heAKgI4PZ3dfBGUZefGCJ1TPDuLHoCzgrMYPb5Rv6TLFuYtyM4rWyHM7hymNdsg5ik2C+UD9VDbA==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [x64]
os: [linux]
libc: [musl]
'@rolldown/binding-openharmony-arm64@1.0.0-rc.9':
resolution: {integrity: sha512-+6zoiF+RRyf5cdlFQP7nm58mq7+/2PFaY2DNQeD4B87N36JzfF/l9mdBkkmTvSYcYPE8tMh/o3cRlsx1ldLfog==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64]
os: [openharmony]
'@rolldown/binding-wasm32-wasi@1.0.0-rc.9':
resolution: {integrity: sha512-rgFN6sA/dyebil3YTlL2evvi/M+ivhfnyxec7AccTpRPccno/rPoNlqybEZQBkcbZu8Hy+eqNJCqfBR8P7Pg8g==}
engines: {node: '>=14.0.0'}
cpu: [wasm32]
'@rolldown/binding-win32-arm64-msvc@1.0.0-rc.9':
resolution: {integrity: sha512-lHVNUG/8nlF1IQk1C0Ci574qKYyty2goMiPlRqkC5R+3LkXDkL5Dhx8ytbxq35m+pkHVIvIxviD+TWLdfeuadA==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64]
os: [win32]
'@rolldown/binding-win32-x64-msvc@1.0.0-rc.9':
resolution: {integrity: sha512-G0oA4+w1iY5AGi5HcDTxWsoxF509hrFIPB2rduV5aDqS9FtDg1CAfa7V34qImbjfhIcA8C+RekocJZA96EarwQ==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [x64]
os: [win32]
'@rolldown/pluginutils@1.0.0-rc.9':
resolution: {integrity: sha512-w6oiRWgEBl04QkFZgmW+jnU1EC9b57Oihi2ot3HNWIQRqgHp5PnYDia5iZ5FF7rpa4EQdiqMDXjlqKGXBhsoXw==}
'@tybys/wasm-util@0.10.1':
resolution: {integrity: sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==}
detect-libc@2.1.2:
resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==}
engines: {node: '>=8'}
fdir@6.5.0:
resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==}
engines: {node: '>=12.0.0'}
peerDependencies:
picomatch: ^3 || ^4
peerDependenciesMeta:
picomatch:
optional: true
fsevents@2.3.3:
resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
os: [darwin]
lightningcss-android-arm64@1.32.0:
resolution: {integrity: sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==}
engines: {node: '>= 12.0.0'}
cpu: [arm64]
os: [android]
lightningcss-darwin-arm64@1.32.0:
resolution: {integrity: sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==}
engines: {node: '>= 12.0.0'}
cpu: [arm64]
os: [darwin]
lightningcss-darwin-x64@1.32.0:
resolution: {integrity: sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==}
engines: {node: '>= 12.0.0'}
cpu: [x64]
os: [darwin]
lightningcss-freebsd-x64@1.32.0:
resolution: {integrity: sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==}
engines: {node: '>= 12.0.0'}
cpu: [x64]
os: [freebsd]
lightningcss-linux-arm-gnueabihf@1.32.0:
resolution: {integrity: sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==}
engines: {node: '>= 12.0.0'}
cpu: [arm]
os: [linux]
lightningcss-linux-arm64-gnu@1.32.0:
resolution: {integrity: sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==}
engines: {node: '>= 12.0.0'}
cpu: [arm64]
os: [linux]
libc: [glibc]
lightningcss-linux-arm64-musl@1.32.0:
resolution: {integrity: sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==}
engines: {node: '>= 12.0.0'}
cpu: [arm64]
os: [linux]
libc: [musl]
lightningcss-linux-x64-gnu@1.32.0:
resolution: {integrity: sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==}
engines: {node: '>= 12.0.0'}
cpu: [x64]
os: [linux]
libc: [glibc]
lightningcss-linux-x64-musl@1.32.0:
resolution: {integrity: sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==}
engines: {node: '>= 12.0.0'}
cpu: [x64]
os: [linux]
libc: [musl]
lightningcss-win32-arm64-msvc@1.32.0:
resolution: {integrity: sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==}
engines: {node: '>= 12.0.0'}
cpu: [arm64]
os: [win32]
lightningcss-win32-x64-msvc@1.32.0:
resolution: {integrity: sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==}
engines: {node: '>= 12.0.0'}
cpu: [x64]
os: [win32]
lightningcss@1.32.0:
resolution: {integrity: sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==}
engines: {node: '>= 12.0.0'}
nanoid@3.3.11:
resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==}
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
hasBin: true
picocolors@1.1.1:
resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
picomatch@4.0.3:
resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==}
engines: {node: '>=12'}
pnpm@10.32.1:
resolution: {integrity: sha512-pwaTjw6JrBRWtlY+q07fHR+vM2jRGR/FxZeQ6W3JGORFarLmfWE94QQ9LoyB+HMD5rQNT/7KnfFe8a1Wc0jyvg==}
engines: {node: '>=18.12'}
hasBin: true
postcss@8.5.8:
resolution: {integrity: sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==}
engines: {node: ^10 || ^12 || >=14}
rolldown@1.0.0-rc.9:
resolution: {integrity: sha512-9EbgWge7ZH+yqb4d2EnELAntgPTWbfL8ajiTW+SyhJEC4qhBbkCKbqFV4Ge4zmu5ziQuVbWxb/XwLZ+RIO7E8Q==}
engines: {node: ^20.19.0 || >=22.12.0}
hasBin: true
source-map-js@1.2.1:
resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
engines: {node: '>=0.10.0'}
tinyglobby@0.2.15:
resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==}
engines: {node: '>=12.0.0'}
tslib@2.8.1:
resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==}
typescript@5.9.3:
resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==}
engines: {node: '>=14.17'}
hasBin: true
vite@8.0.0:
resolution: {integrity: sha512-fPGaRNj9Zytaf8LEiBhY7Z6ijnFKdzU/+mL8EFBaKr7Vw1/FWcTBAMW0wLPJAGMPX38ZPVCVgLceWiEqeoqL2Q==}
engines: {node: ^20.19.0 || >=22.12.0}
hasBin: true
peerDependencies:
'@types/node': ^20.19.0 || >=22.12.0
'@vitejs/devtools': ^0.0.0-alpha.31
esbuild: ^0.27.0
jiti: '>=1.21.0'
less: ^4.0.0
sass: ^1.70.0
sass-embedded: ^1.70.0
stylus: '>=0.54.8'
sugarss: ^5.0.0
terser: ^5.16.0
tsx: ^4.8.1
yaml: ^2.4.2
peerDependenciesMeta:
'@types/node':
optional: true
'@vitejs/devtools':
optional: true
esbuild:
optional: true
jiti:
optional: true
less:
optional: true
sass:
optional: true
sass-embedded:
optional: true
stylus:
optional: true
sugarss:
optional: true
terser:
optional: true
tsx:
optional: true
yaml:
optional: true
snapshots:
'@emnapi/core@1.9.0':
dependencies:
'@emnapi/wasi-threads': 1.2.0
tslib: 2.8.1
optional: true
'@emnapi/runtime@1.9.0':
dependencies:
tslib: 2.8.1
optional: true
'@emnapi/wasi-threads@1.2.0':
dependencies:
tslib: 2.8.1
optional: true
'@napi-rs/wasm-runtime@1.1.1':
dependencies:
'@emnapi/core': 1.9.0
'@emnapi/runtime': 1.9.0
'@tybys/wasm-util': 0.10.1
optional: true
'@oxc-project/runtime@0.115.0': {}
'@oxc-project/types@0.115.0': {}
'@rolldown/binding-android-arm64@1.0.0-rc.9':
optional: true
'@rolldown/binding-darwin-arm64@1.0.0-rc.9':
optional: true
'@rolldown/binding-darwin-x64@1.0.0-rc.9':
optional: true
'@rolldown/binding-freebsd-x64@1.0.0-rc.9':
optional: true
'@rolldown/binding-linux-arm-gnueabihf@1.0.0-rc.9':
optional: true
'@rolldown/binding-linux-arm64-gnu@1.0.0-rc.9':
optional: true
'@rolldown/binding-linux-arm64-musl@1.0.0-rc.9':
optional: true
'@rolldown/binding-linux-ppc64-gnu@1.0.0-rc.9':
optional: true
'@rolldown/binding-linux-s390x-gnu@1.0.0-rc.9':
optional: true
'@rolldown/binding-linux-x64-gnu@1.0.0-rc.9':
optional: true
'@rolldown/binding-linux-x64-musl@1.0.0-rc.9':
optional: true
'@rolldown/binding-openharmony-arm64@1.0.0-rc.9':
optional: true
'@rolldown/binding-wasm32-wasi@1.0.0-rc.9':
dependencies:
'@napi-rs/wasm-runtime': 1.1.1
optional: true
'@rolldown/binding-win32-arm64-msvc@1.0.0-rc.9':
optional: true
'@rolldown/binding-win32-x64-msvc@1.0.0-rc.9':
optional: true
'@rolldown/pluginutils@1.0.0-rc.9': {}
'@tybys/wasm-util@0.10.1':
dependencies:
tslib: 2.8.1
optional: true
detect-libc@2.1.2: {}
fdir@6.5.0(picomatch@4.0.3):
optionalDependencies:
picomatch: 4.0.3
fsevents@2.3.3:
optional: true
lightningcss-android-arm64@1.32.0:
optional: true
lightningcss-darwin-arm64@1.32.0:
optional: true
lightningcss-darwin-x64@1.32.0:
optional: true
lightningcss-freebsd-x64@1.32.0:
optional: true
lightningcss-linux-arm-gnueabihf@1.32.0:
optional: true
lightningcss-linux-arm64-gnu@1.32.0:
optional: true
lightningcss-linux-arm64-musl@1.32.0:
optional: true
lightningcss-linux-x64-gnu@1.32.0:
optional: true
lightningcss-linux-x64-musl@1.32.0:
optional: true
lightningcss-win32-arm64-msvc@1.32.0:
optional: true
lightningcss-win32-x64-msvc@1.32.0:
optional: true
lightningcss@1.32.0:
dependencies:
detect-libc: 2.1.2
optionalDependencies:
lightningcss-android-arm64: 1.32.0
lightningcss-darwin-arm64: 1.32.0
lightningcss-darwin-x64: 1.32.0
lightningcss-freebsd-x64: 1.32.0
lightningcss-linux-arm-gnueabihf: 1.32.0
lightningcss-linux-arm64-gnu: 1.32.0
lightningcss-linux-arm64-musl: 1.32.0
lightningcss-linux-x64-gnu: 1.32.0
lightningcss-linux-x64-musl: 1.32.0
lightningcss-win32-arm64-msvc: 1.32.0
lightningcss-win32-x64-msvc: 1.32.0
nanoid@3.3.11: {}
picocolors@1.1.1: {}
picomatch@4.0.3: {}
pnpm@10.32.1: {}
postcss@8.5.8:
dependencies:
nanoid: 3.3.11
picocolors: 1.1.1
source-map-js: 1.2.1
rolldown@1.0.0-rc.9:
dependencies:
'@oxc-project/types': 0.115.0
'@rolldown/pluginutils': 1.0.0-rc.9
optionalDependencies:
'@rolldown/binding-android-arm64': 1.0.0-rc.9
'@rolldown/binding-darwin-arm64': 1.0.0-rc.9
'@rolldown/binding-darwin-x64': 1.0.0-rc.9
'@rolldown/binding-freebsd-x64': 1.0.0-rc.9
'@rolldown/binding-linux-arm-gnueabihf': 1.0.0-rc.9
'@rolldown/binding-linux-arm64-gnu': 1.0.0-rc.9
'@rolldown/binding-linux-arm64-musl': 1.0.0-rc.9
'@rolldown/binding-linux-ppc64-gnu': 1.0.0-rc.9
'@rolldown/binding-linux-s390x-gnu': 1.0.0-rc.9
'@rolldown/binding-linux-x64-gnu': 1.0.0-rc.9
'@rolldown/binding-linux-x64-musl': 1.0.0-rc.9
'@rolldown/binding-openharmony-arm64': 1.0.0-rc.9
'@rolldown/binding-wasm32-wasi': 1.0.0-rc.9
'@rolldown/binding-win32-arm64-msvc': 1.0.0-rc.9
'@rolldown/binding-win32-x64-msvc': 1.0.0-rc.9
source-map-js@1.2.1: {}
tinyglobby@0.2.15:
dependencies:
fdir: 6.5.0(picomatch@4.0.3)
picomatch: 4.0.3
tslib@2.8.1:
optional: true
typescript@5.9.3: {}
vite@8.0.0:
dependencies:
'@oxc-project/runtime': 0.115.0
lightningcss: 1.32.0
picomatch: 4.0.3
postcss: 8.5.8
rolldown: 1.0.0-rc.9
tinyglobby: 0.2.15
optionalDependencies:
fsevents: 2.3.3

View File

@@ -0,0 +1,58 @@
import { spawnSync } from "node:child_process";
import path from "node:path";
import { fileURLToPath } from "node:url";
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const rootDir = path.resolve(__dirname, "..");
const tscPath = path.join(rootDir, "node_modules", "typescript", "bin", "tsc");
const lessonFiles = [
"01-js-vs-ts/starter.ts",
"01-js-vs-ts/answer.ts",
"02-basic-type-annotations/starter.ts",
"02-basic-type-annotations/answer.ts",
"03-array-and-function-types/starter.ts",
"03-array-and-function-types/answer.ts",
"04-interface-object-shape/starter.ts",
"04-interface-object-shape/answer.ts",
"05-generic-functions/starter.ts",
"05-generic-functions/answer.ts",
"06-union-and-optional-props/starter.ts",
"06-union-and-optional-props/answer.ts",
"07-type-safe-renderer/starter.ts",
"07-type-safe-renderer/answer.ts",
"08-final-mini-app/starter.ts",
"08-final-mini-app/answer.ts",
];
const sharedArgs = [
"--noEmit",
"--pretty",
"false",
"--target",
"ES2020",
"--module",
"ESNext",
"--moduleResolution",
"Bundler",
"--strict",
"--skipLibCheck",
];
for (const lessonFile of lessonFiles) {
const filePath = path.join(rootDir, lessonFile);
console.log(`Checking ${lessonFile}`);
const result = spawnSync(process.execPath, [tscPath, ...sharedArgs, filePath], {
cwd: rootDir,
stdio: "inherit",
});
if (result.status !== 0) {
process.exit(result.status ?? 1);
}
}
console.log("All lesson files passed TypeScript checks.");

View File

@@ -0,0 +1,308 @@
import lesson01Starter from "../01-js-vs-ts/starter.ts?raw";
import lesson01Answer from "../01-js-vs-ts/answer.ts?raw";
import lesson02Starter from "../02-basic-type-annotations/starter.ts?raw";
import lesson02Answer from "../02-basic-type-annotations/answer.ts?raw";
import lesson03Starter from "../03-array-and-function-types/starter.ts?raw";
import lesson03Answer from "../03-array-and-function-types/answer.ts?raw";
import lesson04Starter from "../04-interface-object-shape/starter.ts?raw";
import lesson04Answer from "../04-interface-object-shape/answer.ts?raw";
import lesson05Starter from "../05-generic-functions/starter.ts?raw";
import lesson05Answer from "../05-generic-functions/answer.ts?raw";
import lesson06Starter from "../06-union-and-optional-props/starter.ts?raw";
import lesson06Answer from "../06-union-and-optional-props/answer.ts?raw";
import lesson07Starter from "../07-type-safe-renderer/starter.ts?raw";
import lesson07Answer from "../07-type-safe-renderer/answer.ts?raw";
import lesson08Starter from "../08-final-mini-app/starter.ts?raw";
import lesson08Answer from "../08-final-mini-app/answer.ts?raw";
export interface Lesson {
id: string;
title: string;
focus: string;
summary: string;
starterCode: string;
answerCode: string;
keyPoints: string[];
runDemo: () => string[];
}
function runLesson01(): string[] {
function add(a: number, b: number): number {
return a + b;
}
const result = add(1, 2);
return [
`add(1, 2) => ${result}`,
'如果写成 add(1, "2")TypeScript 会在编码阶段报错。',
];
}
function runLesson02(): string[] {
const age: number = 25;
const userName: string = "李青";
const isLearning: boolean = true;
return [
`age 的类型是 number值为 ${age}`,
`userName 的类型是 string值为 ${userName}`,
`isLearning 的类型是 boolean值为 ${isLearning}`,
];
}
function runLesson03(): string[] {
const scoreList: number[] = [82, 90, 95];
function sum(a: number, b: number): number {
return a + b;
}
const total = sum(88, 12);
return [
`scoreList => [${scoreList.join(", ")}]`,
`sum(88, 12) => ${total}`,
];
}
function runLesson04(): string[] {
interface User {
id: number;
name: string;
age: number;
}
const user: User = {
id: 1,
name: "小王",
age: 22,
};
return [
`User.name => ${user.name}`,
`User.age => ${user.age}`,
"interface 让对象结构在写代码时就固定下来。",
];
}
function runLesson05(): string[] {
function getData<T>(data: T): T {
return data;
}
const text = getData("hello");
const count = getData(100);
return [
`getData("hello") => ${text}`,
`getData(100) => ${count}`,
"泛型可以在复用函数时保留输入和输出的类型关系。",
];
}
function runLesson06(): string[] {
type UserId = number | string;
interface Profile {
id: UserId;
name: string;
age?: number;
}
const profileA: Profile = {
id: 1,
name: "周周",
};
const profileB: Profile = {
id: "u-2",
name: "小林",
age: 24,
};
return [
`profileA.id => ${profileA.id}`,
`profileB.id => ${profileB.id}`,
`profileB.age => ${profileB.age}`,
];
}
function runLesson07(): string[] {
interface Course {
title: string;
lessons: number;
finished: boolean;
}
const courses: Course[] = [
{ title: "TypeScript 基础", lessons: 8, finished: true },
{ title: "接口和泛型", lessons: 6, finished: false },
];
function renderCourseLines(list: Course[]): string[] {
return list.map((course) => {
const status = course.finished ? "已完成" : "学习中";
return `${course.title} - ${course.lessons} 节 - ${status}`;
});
}
return renderCourseLines(courses);
}
function runLesson08(): string[] {
type StudentId = number | string;
interface Course {
title: string;
finished: boolean;
}
interface Student {
id: StudentId;
name: string;
age?: number;
courses: Course[];
}
function pickFirst<T>(list: T[]): T {
return list[0];
}
function formatStudent(student: Student): string {
const courseCount = student.courses.length;
return `${student.name} 当前有 ${courseCount} 门课程,编号是 ${student.id}`;
}
const student: Student = {
id: "stu-1",
name: "林晨",
courses: [
{ title: "基本类型", finished: true },
{ title: "接口", finished: false },
],
};
const firstCourse = pickFirst(student.courses);
return [
formatStudent(student),
`第一门课程:${firstCourse.title}`,
`完成状态:${firstCourse.finished ? "已完成" : "学习中"}`,
];
}
export const lessons: Lesson[] = [
{
id: "01",
title: "JavaScript vs TypeScript",
focus: "理解为什么要引入类型系统",
summary: "从一个最简单的加法函数开始,看 TypeScript 如何把错误提前到编码阶段。",
starterCode: lesson01Starter,
answerCode: lesson01Answer,
keyPoints: [
"TypeScript 在写代码阶段检查参数类型",
"运行前发现问题,比运行后排查问题更省成本",
"最常见的第一步就是给函数参数和返回值加类型",
],
runDemo: runLesson01,
},
{
id: "02",
title: "基本类型标注",
focus: "number / string / boolean",
summary: "把最常见的值类型写清楚,建立最基础的类型意识。",
starterCode: lesson02Starter,
answerCode: lesson02Answer,
keyPoints: [
"基础值先学会标注类型",
"看到报错时先分清期望类型和实际类型",
"能写对基本类型后再往对象和函数走",
],
runDemo: runLesson02,
},
{
id: "03",
title: "数组和函数类型",
focus: "number[] 与函数参数/返回值",
summary: "把类型从单个值扩展到数组和函数,是进入业务代码前必须过的一关。",
starterCode: lesson03Starter,
answerCode: lesson03Answer,
keyPoints: [
"数组里的元素类型要保持一致",
"函数参数和返回值都应该尽量明确",
"函数签名是 TypeScript 最高频的使用场景之一",
],
runDemo: runLesson03,
},
{
id: "04",
title: "interface 对象结构",
focus: "用接口约束对象字段",
summary: "当数据结构开始变复杂时,先定义 shape再写数据和逻辑。",
starterCode: lesson04Starter,
answerCode: lesson04Answer,
keyPoints: [
"对象字段越多,越应该先定义 interface",
"接口让团队协作时更容易知道一个对象该长什么样",
"字段缺失、字段类型不匹配都会更早暴露",
],
runDemo: runLesson04,
},
{
id: "05",
title: "泛型函数",
focus: "保持输入输出的类型关系",
summary: "泛型的重点不是复杂,而是让复用函数在不同类型下依然安全。",
starterCode: lesson05Starter,
answerCode: lesson05Answer,
keyPoints: [
"泛型适合处理同类输入输出关系",
"输入是什么类型,输出就跟着保持什么类型",
"先理解例子,再去记住 <T> 这个写法",
],
runDemo: runLesson05,
},
{
id: "06",
title: "联合类型和可选属性",
focus: "number | string 与 age?",
summary: "业务数据并不总是完全整齐,联合类型和可选属性就是为这种情况准备的。",
starterCode: lesson06Starter,
answerCode: lesson06Answer,
keyPoints: [
"联合类型描述多个合法输入分支",
"可选属性适合那些可能缺失的字段",
"真实业务里常常需要同时使用这两类能力",
],
runDemo: runLesson06,
},
{
id: "07",
title: "类型安全渲染",
focus: "把接口用到列表渲染里",
summary: "进入接近真实业务的场景,用类型约束一组列表数据和渲染函数。",
starterCode: lesson07Starter,
answerCode: lesson07Answer,
keyPoints: [
"先定义 Course再定义 Course[]",
"渲染函数应该接收明确的数据结构",
"数据结构一旦变化,相关代码会一起被提醒",
],
runDemo: runLesson07,
},
{
id: "08",
title: "综合小练习",
focus: "把常见类型能力组合起来",
summary: "把联合类型、接口、可选属性和泛型组合起来,做一个完整的小示例。",
starterCode: lesson08Starter,
answerCode: lesson08Answer,
keyPoints: [
"综合练习的重点是把前面知识点串起来",
"能读懂数据模型,通常就能更稳地写业务代码",
"类型不是目的,减少错误和提升可维护性才是目的",
],
runDemo: runLesson08,
},
];

151
06-typescript/src/main.ts Normal file
View File

@@ -0,0 +1,151 @@
import "./style.css";
import { lessons } from "./lessons";
function getRequiredElement<T extends Element>(
selector: string,
parent: ParentNode = document,
): T {
const element = parent.querySelector<T>(selector);
if (!element) {
throw new Error(`Required element was not found: ${selector}`);
}
return element;
}
const app = getRequiredElement<HTMLDivElement>("#app");
app.innerHTML = `
<div class="shell">
<aside class="sidebar">
<div class="brand">
<p class="eyebrow">Vite + TypeScript</p>
<h1>TypeScript Learning Lab</h1>
<p class="intro">
保留原来的 8 个练习目录,同时给这一章补上一个可直接运行的学习面板。
</p>
</div>
<nav class="lesson-nav" aria-label="TypeScript lessons"></nav>
</aside>
<main class="content">
<section class="hero card">
<p class="badge">06-typescript</p>
<h2 id="lesson-title"></h2>
<p id="lesson-focus" class="focus"></p>
<p id="lesson-summary" class="summary"></p>
</section>
<section class="grid">
<article class="card">
<div class="card-head">
<h3>知识点</h3>
</div>
<ul id="lesson-points" class="point-list"></ul>
</article>
<article class="card">
<div class="card-head">
<h3>运行结果</h3>
<button id="run-demo" class="run-button" type="button">运行演示</button>
</div>
<div id="lesson-output" class="output-panel"></div>
</article>
</section>
<section class="grid code-grid">
<article class="card code-card">
<div class="card-head">
<h3>Starter</h3>
</div>
<pre><code id="starter-code"></code></pre>
</article>
<article class="card code-card">
<div class="card-head">
<h3>Answer</h3>
</div>
<pre><code id="answer-code"></code></pre>
</article>
</section>
</main>
</div>
`;
const nav = getRequiredElement<HTMLElement>(".lesson-nav");
const title = getRequiredElement<HTMLElement>("#lesson-title");
const focus = getRequiredElement<HTMLElement>("#lesson-focus");
const summary = getRequiredElement<HTMLElement>("#lesson-summary");
const pointList = getRequiredElement<HTMLElement>("#lesson-points");
const output = getRequiredElement<HTMLElement>("#lesson-output");
const starterCode = getRequiredElement<HTMLElement>("#starter-code");
const answerCode = getRequiredElement<HTMLElement>("#answer-code");
const runButton = getRequiredElement<HTMLButtonElement>("#run-demo");
let activeLessonId = lessons[0]?.id ?? "";
function renderLessonList(): void {
nav.innerHTML = lessons
.map((lesson) => {
const isActive = lesson.id === activeLessonId;
return `
<button
class="lesson-link ${isActive ? "is-active" : ""}"
type="button"
data-id="${lesson.id}"
>
<span class="lesson-index">${lesson.id}</span>
<span class="lesson-meta">
<strong>${lesson.title}</strong>
<small>${lesson.focus}</small>
</span>
</button>
`;
})
.join("");
}
function renderOutput(lines: string[]): void {
output.innerHTML = lines
.map((line) => `<div class="output-line">${line}</div>`)
.join("");
}
function renderActiveLesson(): void {
const lesson = lessons.find((item) => item.id === activeLessonId);
if (!lesson) {
return;
}
title.textContent = `${lesson.id}. ${lesson.title}`;
focus.textContent = lesson.focus;
summary.textContent = lesson.summary;
pointList.innerHTML = lesson.keyPoints
.map((item) => `<li>${item}</li>`)
.join("");
starterCode.textContent = lesson.starterCode;
answerCode.textContent = lesson.answerCode;
renderOutput(lesson.runDemo());
renderLessonList();
}
nav.addEventListener("click", (event) => {
const target = event.target as HTMLElement;
const button = target.closest<HTMLButtonElement>("[data-id]");
if (!button) {
return;
}
activeLessonId = button.dataset.id ?? activeLessonId;
renderActiveLesson();
});
runButton.addEventListener("click", () => {
const lesson = lessons.find((item) => item.id === activeLessonId);
if (!lesson) {
return;
}
renderOutput(lesson.runDemo());
});
renderActiveLesson();

237
06-typescript/src/style.css Normal file
View File

@@ -0,0 +1,237 @@
:root {
font-family: "IBM Plex Sans", "PingFang SC", "Hiragino Sans GB", sans-serif;
line-height: 1.5;
font-weight: 400;
color: #f7f4ec;
background:
radial-gradient(circle at top left, rgba(255, 191, 73, 0.22), transparent 24rem),
radial-gradient(circle at bottom right, rgba(86, 154, 255, 0.16), transparent 26rem),
#11131a;
font-synthesis: none;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
* {
box-sizing: border-box;
}
body {
margin: 0;
min-width: 320px;
min-height: 100vh;
}
button,
code,
pre {
font: inherit;
}
#app {
min-height: 100vh;
}
.shell {
display: grid;
grid-template-columns: 320px minmax(0, 1fr);
min-height: 100vh;
}
.sidebar {
padding: 28px 20px;
border-right: 1px solid rgba(255, 255, 255, 0.08);
background: rgba(9, 10, 14, 0.62);
backdrop-filter: blur(18px);
}
.brand {
margin-bottom: 24px;
}
.eyebrow,
.badge {
display: inline-flex;
align-items: center;
gap: 8px;
margin: 0 0 12px;
padding: 6px 10px;
border-radius: 999px;
background: rgba(255, 191, 73, 0.14);
color: #ffcc6a;
font-size: 12px;
letter-spacing: 0.08em;
text-transform: uppercase;
}
.brand h1 {
margin: 0 0 10px;
font-size: 28px;
line-height: 1.1;
}
.intro,
.summary {
margin: 0;
color: rgba(247, 244, 236, 0.72);
}
.lesson-nav {
display: grid;
gap: 10px;
}
.lesson-link {
display: flex;
align-items: flex-start;
gap: 12px;
width: 100%;
padding: 14px 12px;
border: 1px solid rgba(255, 255, 255, 0.08);
border-radius: 18px;
background: rgba(255, 255, 255, 0.03);
color: inherit;
text-align: left;
cursor: pointer;
transition:
transform 180ms ease,
border-color 180ms ease,
background 180ms ease;
}
.lesson-link:hover,
.lesson-link.is-active {
transform: translateY(-1px);
border-color: rgba(255, 191, 73, 0.42);
background: rgba(255, 191, 73, 0.1);
}
.lesson-index {
min-width: 40px;
padding: 8px 0;
border-radius: 12px;
background: rgba(255, 255, 255, 0.06);
text-align: center;
font-weight: 700;
}
.lesson-meta {
display: grid;
gap: 4px;
}
.lesson-meta small {
color: rgba(247, 244, 236, 0.64);
}
.content {
padding: 28px;
}
.hero {
margin-bottom: 20px;
}
.focus {
margin: 0 0 10px;
color: #ffcc6a;
font-weight: 600;
}
.grid {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 20px;
margin-bottom: 20px;
}
.code-grid {
align-items: start;
}
.card {
padding: 20px;
border: 1px solid rgba(255, 255, 255, 0.08);
border-radius: 28px;
background: rgba(15, 17, 24, 0.72);
box-shadow: 0 18px 48px rgba(0, 0, 0, 0.22);
}
.card-head {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
margin-bottom: 14px;
}
.card-head h3 {
margin: 0;
font-size: 18px;
}
.point-list {
margin: 0;
padding-left: 18px;
color: rgba(247, 244, 236, 0.84);
}
.point-list li + li {
margin-top: 10px;
}
.output-panel {
display: grid;
gap: 10px;
}
.output-line {
padding: 12px 14px;
border-radius: 16px;
background: rgba(86, 154, 255, 0.12);
color: #d8e9ff;
}
.run-button {
padding: 10px 14px;
border: 0;
border-radius: 999px;
background: linear-gradient(135deg, #ffbf49, #ff8f48);
color: #1d1302;
font-weight: 700;
cursor: pointer;
}
.code-card pre {
overflow: auto;
margin: 0;
padding: 18px;
border-radius: 20px;
background: #0b0d12;
}
.code-card code {
font-family: "JetBrains Mono", "SFMono-Regular", monospace;
font-size: 13px;
white-space: pre;
}
@media (max-width: 960px) {
.shell {
grid-template-columns: 1fr;
}
.sidebar {
border-right: 0;
border-bottom: 1px solid rgba(255, 255, 255, 0.08);
}
.grid {
grid-template-columns: 1fr;
}
.content {
padding: 20px;
}
}

1
06-typescript/src/vite-env.d.ts vendored Normal file
View File

@@ -0,0 +1 @@
/// <reference types="vite/client" />

View File

@@ -0,0 +1,16 @@
{
"compilerOptions": {
"target": "ES2020",
"useDefineForClassFields": true,
"module": "ESNext",
"lib": ["ES2020", "DOM", "DOM.Iterable"],
"moduleResolution": "Bundler",
"types": ["vite/client"],
"resolveJsonModule": true,
"isolatedModules": true,
"strict": true,
"noEmit": true,
"skipLibCheck": true
},
"include": ["./src/**/*.ts", "./src/**/*.d.ts"]
}