第四章是项目实践,这就好比你学了很多做菜的技巧,现在要亲自下厨做几道菜来检验和提升自己的厨艺。下面给你介绍小型命令行项目和 Web 应用项目这两类实践,帮你巩固 TypeScript 知识。
小型命令行项目 - 简易文件搜索工具
项目描述
这个简易文件搜索工具就像是一个小侦探,能在你指定的文件夹里帮你找出包含特定关键字的文件。你在命令行里告诉它要搜索哪个文件夹,以及要找的关键字,它就会把符合条件的文件列出来。
巩固要点
这个项目能让你熟悉如何处理命令行输入、进行文件系统操作,还能加深对 TypeScript 里异步操作的理解。
代码示例
typescript">import fs from 'fs';
import path from 'path';
import readline from 'readline';
// 创建一个 readline 接口,用于和用户在命令行交互
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
// 定义一个异步函数来搜索文件
async function searchFiles(folderPath: string, keyword: string) {
try {
// 读取指定文件夹下的所有文件和子文件夹
const files = await fs.promises.readdir(folderPath);
for (const file of files) {
const filePath = path.join(folderPath, file);
// 检查当前路径是文件还是文件夹
const stat = await fs.promises.stat(filePath);
if (stat.isDirectory()) {
// 如果是文件夹,递归调用 searchFiles 函数继续搜索
await searchFiles(filePath, keyword);
} else {
// 如果是文件,读取文件内容
const data = await fs.promises.readFile(filePath, 'utf8');
if (data.includes(keyword)) {
// 如果文件内容包含关键字,打印文件路径
console.log(filePath);
}
}
}
} catch (error) {
// 处理可能出现的错误
console.error('搜索过程中出现错误:', error);
}
}
// 询问用户要搜索的文件夹路径
rl.question('请输入要搜索的文件夹路径: ', (folderPath) => {
// 询问用户要搜索的关键字
rl.question('请输入要搜索的关键字: ', (keyword) => {
// 调用搜索函数开始搜索
searchFiles(folderPath, keyword).then(() => {
// 搜索完成后关闭 readline 接口
rl.close();
});
});
});
代码解释
- 导入模块:导入了
fs
(用于文件系统操作)、path
(用于处理文件路径)和readline
(用于和用户在命令行交互)这几个模块。 - 创建 readline 接口:通过
readline.createInterface
创建了一个接口,方便我们从命令行获取用户输入。 - 定义搜索函数:
searchFiles
是一个异步函数,它会读取指定文件夹下的所有文件和子文件夹。如果遇到文件夹,就递归调用自己继续搜索;如果遇到文件,就读取文件内容,检查是否包含关键字。 - 获取用户输入:通过
rl.question
询问用户要搜索的文件夹路径和关键字,然后调用searchFiles
函数开始搜索。 - 错误处理:使用
try...catch
块来捕获和处理搜索过程中可能出现的错误。
Web 应用项目 - 简单的博客列表展示
项目描述
这是一个简单的 Web 应用,它会在网页上展示一个博客文章列表。每篇文章有标题、作者和简要内容,你可以通过修改代码里的文章数据来更新列表。
巩固要点
这个项目能让你学会如何结合 HTML、CSS 和 TypeScript 来开发 Web 应用,掌握 DOM 操作(也就是操作网页上的元素)和事件处理。
代码示例
HTML 文件(index.html
)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>简单博客列表</title>
<style>
/* 简单的样式,让博客列表看起来更美观 */
body {
font-family: Arial, sans-serif;
padding: 20px;
}
.blog-post {
border: 1px solid #ccc;
padding: 10px;
margin-bottom: 10px;
}
</style>
</head>
<body>
<h1>博客列表</h1>
<div id="blog-list"></div>
<!-- 引入编译后的 JavaScript 文件 -->
<script src="main.js"></script>
</body>
</html>
TypeScript 文件(main.ts
)
typescript">// 定义博客文章的类型
type BlogPost = {
title: string;
author: string;
content: string;
};
// 定义一些博客文章数据
const blogPosts: BlogPost[] = [
{
title: "第一篇博客",
author: "张三",
content: "这是我的第一篇博客,分享一些生活趣事。"
},
{
title: "技术分享",
author: "李四",
content: "今天来分享一些前端开发的小技巧。"
}
];
// 获取用于展示博客列表的 DOM 元素
const blogList = document.getElementById('blog-list');
if (blogList) {
// 遍历博客文章数组
blogPosts.forEach(post => {
// 创建一个 div 元素来包裹每篇博客文章
const postDiv = document.createElement('div');
postDiv.classList.add('blog-post');
// 创建标题元素
const titleElement = document.createElement('h2');
titleElement.textContent = post.title;
// 创建作者元素
const authorElement = document.createElement('p');
authorElement.textContent = `作者: ${post.author}`;
// 创建内容元素
const contentElement = document.createElement('p');
contentElement.textContent = post.content;
// 将标题、作者和内容元素添加到 div 元素中
postDiv.appendChild(titleElement);
postDiv.appendChild(authorElement);
postDiv.appendChild(contentElement);
// 将包含博客文章信息的 div 元素添加到博客列表中
blogList.appendChild(postDiv);
});
}
代码解释
- HTML 文件:创建了一个简单的网页结构,有一个标题和一个用于展示博客列表的
div
元素,同时引入了编译后的 JavaScript 文件。 - TypeScript 文件:
- 定义类型:使用
type
定义了BlogPost
类型,规定了博客文章应该包含的属性。 - 准备数据:创建了一个
blogPosts
数组,里面存放了一些博客文章的数据。 - 获取 DOM 元素:通过
document.getElementById
获取用于展示博客列表的div
元素。 - 遍历数据并创建元素:使用
forEach
遍历blogPosts
数组,为每篇文章创建相应的div
、h2
、p
元素,并将它们添加到网页中。
- 定义类型:使用
通过这两个项目实践,你可以更好地掌握 TypeScript 在不同场景下的应用,提升自己的编程能力。
那么,除了文件搜索工具,还可以用 TypeScript 实现哪些小型命令行项目?
除了文件搜索工具外,还有许多适合用 TypeScript 实现的小型命令行项目,以下为你详细介绍:
1. 任务管理工具
- 功能描述:这是一个能帮助你管理日常任务的工具。你可以添加新任务,为每个任务设置截止日期和优先级;查看当前所有未完成的任务列表,列表会按照优先级或截止日期排序;标记任务为已完成,完成的任务会被归档,你也能随时查看历史完成的任务;还可以删除不再需要的任务。
- 巩固要点:需要使用数组和对象来存储任务数据,学会使用条件判断和循环来处理任务的筛选和排序,掌握文件系统操作来实现任务数据的持久化存储。
- 代码示例:
typescript">import fs from 'fs';
import readline from 'readline';
// 定义任务类型
type Task = {
id: number;
description: string;
completed: boolean;
};
// 读取任务数据
function readTasks(): Task[] {
try {
const data = fs.readFileSync('tasks.json', 'utf8');
return JSON.parse(data);
} catch (error) {
return [];
}
}
// 保存任务数据
function saveTasks(tasks: Task[]) {
fs.writeFileSync('tasks.json', JSON.stringify(tasks, null, 2));
}
// 创建新任务
function createTask(description: string) {
const tasks = readTasks();
const newTask: Task = {
id: tasks.length > 0 ? tasks[tasks.length - 1].id + 1 : 1,
description,
completed: false
};
tasks.push(newTask);
saveTasks(tasks);
console.log('任务已添加');
}
// 列出所有任务
function listTasks() {
const tasks = readTasks();
tasks.forEach(task => {
console.log(`${task.id}. [${task.completed ? 'X' : ' '}] ${task.description}`);
});
}
// 标记任务为已完成
function completeTask(id: number) {
const tasks = readTasks();
const task = tasks.find(t => t.id === id);
if (task) {
task.completed = true;
saveTasks(tasks);
console.log('任务已标记为完成');
} else {
console.log('未找到该任务');
}
}
// 命令行交互
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
rl.question('请输入命令 (add <描述> | list | complete <id>): ', (command) => {
const [action, ...args] = command.split(' ');
switch (action) {
case 'add':
createTask(args.join(' '));
break;
case 'list':
listTasks();
break;
case 'complete':
const id = parseInt(args[0]);
if (!isNaN(id)) {
completeTask(id);
} else {
console.log('无效的任务 ID');
}
break;
default:
console.log('无效的命令');
}
rl.close();
});
2. 单词拼写检查器
- 功能描述:该工具可以检查用户输入的单词是否拼写正确。它会有一个内置的词典,当用户输入一个单词后,工具会在词典中查找该单词,如果找不到,就会提示拼写错误,并可以提供一些可能正确的拼写建议。
- 巩固要点:要学会使用字符串处理方法来比较单词,掌握数据结构如数组或集合来存储词典,使用算法来生成可能的拼写建议。
- 代码示例:
typescript">import readline from 'readline';
// 简单的词典
const dictionary = ['apple', 'banana', 'cherry', 'date'];
function checkSpelling(word: string) {
if (dictionary.includes(word)) {
console.log(`${word} 拼写正确`);
} else {
console.log(`${word} 拼写错误`);
// 简单示例:查找可能的建议
const suggestions = dictionary.filter(dictWord => {
const distance = levenshteinDistance(word, dictWord);
return distance <= 1;
});
if (suggestions.length > 0) {
console.log(`可能的正确拼写: ${suggestions.join(', ')}`);
}
}
}
// 计算莱文斯坦距离
function levenshteinDistance(a: string, b: string): number {
const m = a.length;
const n = b.length;
const dp: number[][] = Array.from({ length: m + 1 }, () => Array(n + 1).fill(0));
for (let i = 0; i <= m; i++) {
for (let j = 0; j <= n; j++) {
if (i === 0) {
dp[i][j] = j;
} else if (j === 0) {
dp[i][j] = i;
} else if (a[i - 1] === b[j - 1]) {
dp[i][j] = dp[i - 1][j - 1];
} else {
dp[i][j] = 1 + Math.min(dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1]);
}
}
}
return dp[m][n];
}
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
rl.question('请输入一个单词进行拼写检查: ', (word) => {
checkSpelling(word.trim().toLowerCase());
rl.close();
});
3. 汇率转换器
- 功能描述:这个工具能让你方便地进行不同货币之间的汇率转换。你可以设置不同货币的汇率,输入要转换的金额和目标货币,工具会计算并输出转换后的金额。
- 巩固要点:运用数学运算进行金额的转换,使用对象来存储不同货币的汇率信息,处理用户输入的金额和货币类型。
- 代码示例:
typescript">import readline from 'readline';
// 汇率数据
const exchangeRates = {
USD: 1,
EUR: 0.9,
GBP: 0.8,
JPY: 110
};
function convertCurrency(amount: number, fromCurrency: string, toCurrency: string) {
if (!exchangeRates[fromCurrency] || !exchangeRates[toCurrency]) {
console.log('不支持的货币类型');
return;
}
const convertedAmount = amount * (exchangeRates[toCurrency] / exchangeRates[fromCurrency]);
console.log(`${amount} ${fromCurrency} = ${convertedAmount.toFixed(2)} ${toCurrency}`);
}
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
rl.question('请输入要转换的金额: ', (amountStr) => {
const amount = parseFloat(amountStr);
if (isNaN(amount)) {
console.log('无效的金额');
rl.close();
return;
}
rl.question('请输入源货币 (如 USD): ', (fromCurrency) => {
rl.question('请输入目标货币 (如 EUR): ', (toCurrency) => {
convertCurrency(amount, fromCurrency.toUpperCase(), toCurrency.toUpperCase());
rl.close();
});
});
});
4. 随机密码生成器
- 功能描述:根据用户指定的密码长度和包含的字符类型(如字母、数字、特殊字符)生成随机密码。用户可以通过命令行参数来控制密码的复杂度和长度。
- 巩固要点:掌握随机数生成和字符串拼接的方法,根据用户输入的条件生成符合要求的密码。
- 代码示例:
typescript">import readline from 'readline';
const lowercase = 'abcdefghijklmnopqrstuvwxyz';
const uppercase = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
const numbers = '0123456789';
const symbols = '!@#$%^&*()_+-=[]{}|;:\",./<>?';
function generatePassword(length: number, includeUppercase: boolean, includeNumbers: boolean, includeSymbols: boolean) {
let characters = lowercase;
if (includeUppercase) characters += uppercase;
if (includeNumbers) characters += numbers;
if (includeSymbols) characters += symbols;
let password = '';
for (let i = 0; i < length; i++) {
const randomIndex = Math.floor(Math.random() * characters.length);
password += characters.charAt(randomIndex);
}
return password;
}
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
rl.question('请输入密码长度: ', (lengthStr) => {
const length = parseInt(lengthStr);
if (isNaN(length) || length <= 0) {
console.log('无效的密码长度');
rl.close();
return;
}
rl.question('是否包含大写字母 (y/n): ', (includeUppercaseStr) => {
rl.question('是否包含数字 (y/n): ', (includeNumbersStr) => {
rl.question('是否包含特殊字符 (y/n): ', (includeSymbolsStr) => {
const includeUppercase = includeUppercaseStr.toLowerCase() === 'y';
const includeNumbers = includeNumbersStr.toLowerCase() === 'y';
const includeSymbols = includeSymbolsStr.toLowerCase() === 'y';
const password = generatePassword(length, includeUppercase, includeNumbers, includeSymbols);
console.log('生成的密码: ', password);
rl.close();
});
});
});
});