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:
21
06-typescript/01-js-vs-ts/README.md
Normal file
21
06-typescript/01-js-vs-ts/README.md
Normal file
@@ -0,0 +1,21 @@
|
||||
# 练习 1:JavaScript 和 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)
|
||||
10
06-typescript/01-js-vs-ts/answer.ts
Normal file
10
06-typescript/01-js-vs-ts/answer.ts
Normal 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。
|
||||
11
06-typescript/01-js-vs-ts/starter.ts
Normal file
11
06-typescript/01-js-vs-ts/starter.ts
Normal 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");
|
||||
21
06-typescript/02-basic-type-annotations/README.md
Normal file
21
06-typescript/02-basic-type-annotations/README.md
Normal 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)
|
||||
5
06-typescript/02-basic-type-annotations/answer.ts
Normal file
5
06-typescript/02-basic-type-annotations/answer.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
let age: number = 21;
|
||||
let userName: string = "林晨";
|
||||
let isPaid: boolean = true;
|
||||
|
||||
console.log(`${userName} 今年 ${age} 岁,课程付费状态:${isPaid}`);
|
||||
7
06-typescript/02-basic-type-annotations/starter.ts
Normal file
7
06-typescript/02-basic-type-annotations/starter.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
let age: number = 18;
|
||||
let userName: string = "Tom";
|
||||
let isPaid: boolean = true;
|
||||
|
||||
// 任务:
|
||||
// 1. 修改上面的值,让它们更像一个学习者资料
|
||||
// 2. 输出一句完整信息
|
||||
22
06-typescript/03-array-and-function-types/README.md
Normal file
22
06-typescript/03-array-and-function-types/README.md
Normal 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)
|
||||
10
06-typescript/03-array-and-function-types/answer.ts
Normal file
10
06-typescript/03-array-and-function-types/answer.ts
Normal 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);
|
||||
11
06-typescript/03-array-and-function-types/starter.ts
Normal file
11
06-typescript/03-array-and-function-types/starter.ts
Normal 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 结果
|
||||
22
06-typescript/04-interface-object-shape/README.md
Normal file
22
06-typescript/04-interface-object-shape/README.md
Normal file
@@ -0,0 +1,22 @@
|
||||
# 练习 4:interface 描述对象结构
|
||||
|
||||
## 目标
|
||||
|
||||
学会用 `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)
|
||||
14
06-typescript/04-interface-object-shape/answer.ts
Normal file
14
06-typescript/04-interface-object-shape/answer.ts
Normal 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);
|
||||
15
06-typescript/04-interface-object-shape/starter.ts
Normal file
15
06-typescript/04-interface-object-shape/starter.ts
Normal 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
|
||||
21
06-typescript/05-generic-functions/README.md
Normal file
21
06-typescript/05-generic-functions/README.md
Normal 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)
|
||||
9
06-typescript/05-generic-functions/answer.ts
Normal file
9
06-typescript/05-generic-functions/answer.ts
Normal 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);
|
||||
9
06-typescript/05-generic-functions/starter.ts
Normal file
9
06-typescript/05-generic-functions/starter.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
function getData<T>(data: T): T {
|
||||
// 返回 data
|
||||
return data;
|
||||
}
|
||||
|
||||
// 任务:
|
||||
// 1. 用 number 调用
|
||||
// 2. 用 string 调用
|
||||
// 3. 输出结果
|
||||
22
06-typescript/06-union-and-optional-props/README.md
Normal file
22
06-typescript/06-union-and-optional-props/README.md
Normal 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)
|
||||
19
06-typescript/06-union-and-optional-props/answer.ts
Normal file
19
06-typescript/06-union-and-optional-props/answer.ts
Normal 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);
|
||||
12
06-typescript/06-union-and-optional-props/starter.ts
Normal file
12
06-typescript/06-union-and-optional-props/starter.ts
Normal 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. 输出它们
|
||||
22
06-typescript/07-type-safe-renderer/README.md
Normal file
22
06-typescript/07-type-safe-renderer/README.md
Normal 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)
|
||||
19
06-typescript/07-type-safe-renderer/answer.ts
Normal file
19
06-typescript/07-type-safe-renderer/answer.ts
Normal 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));
|
||||
19
06-typescript/07-type-safe-renderer/starter.ts
Normal file
19
06-typescript/07-type-safe-renderer/starter.ts
Normal 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. 输出结果
|
||||
24
06-typescript/08-final-mini-app/README.md
Normal file
24
06-typescript/08-final-mini-app/README.md
Normal 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)
|
||||
36
06-typescript/08-final-mini-app/answer.ts
Normal file
36
06-typescript/08-final-mini-app/answer.ts
Normal 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);
|
||||
36
06-typescript/08-final-mini-app/starter.ts
Normal file
36
06-typescript/08-final-mini-app/starter.ts
Normal 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
170
06-typescript/README.md
Normal 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
15
06-typescript/index.html
Normal 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
913
06-typescript/package-lock.json
generated
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
21
06-typescript/package.json
Normal file
21
06-typescript/package.json
Normal 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
518
06-typescript/pnpm-lock.yaml
generated
Normal 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
|
||||
58
06-typescript/scripts/check-lessons.mjs
Normal file
58
06-typescript/scripts/check-lessons.mjs
Normal 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.");
|
||||
308
06-typescript/src/lessons.ts
Normal file
308
06-typescript/src/lessons.ts
Normal 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
151
06-typescript/src/main.ts
Normal 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
237
06-typescript/src/style.css
Normal 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
1
06-typescript/src/vite-env.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/// <reference types="vite/client" />
|
||||
16
06-typescript/tsconfig.json
Normal file
16
06-typescript/tsconfig.json
Normal 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"]
|
||||
}
|
||||
Reference in New Issue
Block a user