创建 TypeScript 项目,显示第一件商品
这门课面向第一次学习 TypeScript 的开发者。从创建项目开始,逐步学习怎样描述数据、检查函数调用、组织模块和处理接口响应。学习的目标是:面对一个业务需求,能够设计合适的数据类型,实现功能,并根据类型提示和运行结果定位问题。
课程从简单的页面逐步推进到完整功能。每引入一种写法,都会结合代码解释它的作用、为什么需要它,以及怎样核对结果;常见错误也会配合具体提示说明原因,帮助你理解类型检查能够发现什么、哪些问题仍需要程序在运行时处理。
贯穿课程的案例是一个商品管理页面。随着学习进度,逐步加入列表展示、库存调整、表单和分类管理,再接入课程提供的接口服务。接口部分重点学习前端怎样获取、校验和提交数据,不讲后端服务开发。
本节先完成第一步:创建并启动 TypeScript 项目,让浏览器显示商品名称,再运行类型检查,对照一次错误赋值,观察类型检查结果与页面运行之间的区别。
开始前需要会什么
“入门”指 TypeScript 开发入门,不要求学过 React 或 Vue。开始前需要了解:
- HTML 的标题、段落和页面基本结构。
- CSS 的选择器、颜色、字体及内外边距。
- JavaScript 的变量、对象、数组、函数和属性访问。
TypeScript、Vite 和 Node.js 各自负责什么
TypeScript 是在 JavaScript 基础上增加类型语法的语言。例如,可以约定商品名称是字符串;把数字填到该位置时,类型检查工具就能报告不符合约定的地方。已有的 JavaScript 基础仍会用到,类型标注的具体写法在本节代码中介绍。查阅语言手册时,可以从 TypeScript 官方中文文档 进入;部分条目仍为英文。
Vite 提供开发服务器和构建工具。开发时,它将项目源码处理成浏览器可以执行的 JavaScript;保存文件后,页面可以随之更新。本课程使用它创建和运行 TypeScript 项目。
Node.js让 JavaScript 可以在浏览器之外运行,本节用它运行 Vite 和 TypeScript 检查工具。随 Node.js 安装的npm 用来下载项目依赖、执行项目命令。
开发服务和类型检查分别负责页面运行与类型错误报告,启动开发服务不会自动完成类型检查。本节会分别运行这两项操作,并对照它们的结果。
安装并检查开发工具
以下步骤使用 Windows 10/11、VS Code 和命令提示符终端。macOS、Linux 的项目命令相同,安装 Node.js 时选择对应系统的版本;项目目录可使用主目录下的 Course,不照抄 Windows 的磁盘路径。
安装 Node.js 与 VS Code
从 Node.js 官方下载页 下载 Node.js 24 LTS 的安装程序。Windows 普通 Intel/AMD 电脑选择 x64,ARM 电脑选择 ARM64,按向导安装并保留 npm 与添加到 PATH 的选项。
PATH 是系统查找命令的目录列表,安装时添加到 PATH 后,终端才能找到 node 和 npm。
从 VS Code 官方下载页(英文) 下载并安装编辑器。安装 Node.js 后重新打开 VS Code,使新终端读取更新后的 PATH。
本节实测使用 Node.js 24.16.0、npm 11.13.0。Vite 官方要求 Node.js 20.19+ 或 22.12+,Node.js 24 LTS 满足要求;已有 Node.js 时先检查版本,再决定是否重装。要求来源见 Vite 官方中文入门文档。
选择终端并查看版本
在 VS Code 中按 Ctrl+Shift+P 打开命令面板,输入 Terminal: Select Default Profile,选择 Command Prompt(命令提示符)。再通过 Terminal > New Terminal 新建终端。
这里使用命令提示符,是为了避免 Windows 的 PowerShell 脚本执行策略阻止 npm 启动。选择默认终端不会改变已经打开的终端,需要新建一个。后续 Windows 命令都在这个终端里执行,无需调整系统执行策略。
逐条执行:
node --version
npm --version本节验证环境分别输出:
v24.16.0
11.13.0如果提示不是内部或外部命令,先关闭并重新打开 VS Code;仍然失败时,重新运行 Node.js 安装程序,确认安装了 npm 并添加到 PATH。
创建 TypeScript 项目
生成项目文件
在 Windows 文件资源管理器中新建存放课程工程的文件夹,例如 D:\Course。没有 D 盘时选择其他位置;不要在现有业务项目里面创建课程工程。
在 VS Code 中选择 File > Open Folder,打开 Course 文件夹,再新建命令提示符终端。确认终端当前目录是刚打开的文件夹,在这里执行:
npx --yes create-vite@9.2.1 ts-demo --template vanilla-ts --no-interactive这条命令会在当前目录下新建 ts-demo:
- npx 负责运行项目创建工具;
--yes表示允许下载并运行命令中指定版本的工具。 create-vite@9.2.1是工具名称和版本,不是 TypeScript 或 Vite 的版本。ts-demo是新项目的文件夹名。--template vanilla-ts选择不带前端框架的 TypeScript 模板。--no-interactive按指定模板创建,不进入框架选择问答。
创建完成后,左侧出现 ts-demo 文件夹,终端提示进入目录、安装依赖和启动服务。接下来通过 VS Code 打开这个新目录,不在 Course 中执行项目命令。
如果已有同名文件夹,先核对是不是此前创建的课程项目。已有项目可以直接打开,不要选择清空包含自己文件的目录。
固定依赖并安装
选择 File > Open Folder,打开刚创建的 ts-demo 文件夹,例如 D:\Course\ts-demo;使用其他磁盘或目录时,以实际创建位置为准。左侧顶层目录应变成 ts-demo,其中能看到 package.json。新建终端,后续命令都在这个项目根目录执行。
package.json 记录项目名称、命令和依赖。为了使用本节实际验证的版本,将它的全部内容替换为:
{
"name": "ts-demo",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"check": "tsc --noEmit",
"build": "tsc && vite build",
"preview": "vite preview"
},
"devDependencies": {
"typescript": "6.0.3",
"vite": "8.3.4"
}
}本节先使用 dev 和 check:前者启动开发服务,后者检查类型。devDependencies 中的两个包都是开发工具;type: "module" 指定项目使用模块方式组织代码,暂时保留即可。
保存文件,在终端执行:
npm install等待命令完成、终端重新出现可输入命令的提示符。安装后会生成 node_modules 和 package-lock.json:前者保存依赖代码,后者记录实际安装的版本,后续保留锁文件,不手动修改依赖目录。
启动并打开页面
在项目根目录执行:
npm run devnpm run dev 读取 package.json 的 scripts.dev,执行其中的 vite,启动开发服务。
终端显示启动信息后,在 Chrome 或 Edge 中打开 Local 一行给出的地址。默认端口未被占用时通常是:
http://localhost:5173/当前模板页面包含 TypeScript、Vite 标识、Get started 标题和计数按钮。看到它说明模板能运行;接下来把它换成商品页面。
localhost 表示当前电脑,端口号用于区分服务。默认端口被占用时,Vite 会尝试其他端口,以终端显示的地址为准,不要双击 index.html。
服务启动后,这个终端会持续显示运行信息,暂时保留它。后面执行类型检查时,再新建第二个终端。
显示第一件商品
页面部分修改三个文件:
| 文件 | 用途 |
|---|---|
src/main.ts |
创建页面元素,写入商品名称 |
index.html |
页面入口,加载 src/main.ts |
src/style.css |
设置页面的通用样式 |
文件名中的 .ts 表示 TypeScript 源码。下面在 JavaScript 的变量定义中加入一处类型标注,再用 DOM 操作创建页面元素。
直接在 index.html 中写 <p>矿泉水</p> 也能显示商品名称。后面商品会变成一组数据,需要由代码按数据生成页面,因此从本节开始就把内容交给 main.ts 创建。
编写 main.ts
将 src/main.ts 的全部内容替换为:
import './style.css'
const productName: string = '矿泉水'
const page = document.createElement('main')
page.className = 'page'
const title = document.createElement('h1')
title.textContent = '商品管理'
const productNameElement = document.createElement('p')
productNameElement.textContent = productName
page.append(title, productNameElement)
document.body.append(page)保存后,原来的模板内容被移除,页面出现“商品管理”和“矿泉水”,位置和颜色暂时仍受模板样式影响。下面先看新增代码负责什么,再替换入口和样式。
import './style.css' 加载同目录的样式文件,./ 表示当前文件所在目录。
const productName: string = '矿泉水' 定义商品名称。冒号后的 string 是类型标注,表示这里约定保存字符串;等号右侧是实际的值。删去 : string 仍然可以运行,TypeScript 会根据初始值推断类型,本节保留标注以便观察检查结果。
浏览器用 DOM 对象表示页面元素。这里使用几个基本操作:
document.createElement('main')创建一个main元素,返回它对应的对象。创建后还没有放到页面上。className = 'page'为它设置 CSS 类名,对应 HTML 中的class="page"。createElement('h1')和createElement('p')创建标题与段落,保存到各自的变量中。textContent设置元素中的文字。商品名称按文字显示,不把其中的内容解析成 HTML 标签。page.append(title, productNameElement)将标题和段落按顺序放入主体;document.body.append(page)再把主体放入页面。
main 表示页面主要内容,h1 是内容标题,p 是段落。只创建对象而不调用 append,元素不会出现在页面里。用变量保留元素,是为了接着设置文字、类名和放置位置。
替换页面入口
将项目根目录 index.html 的全部内容替换为:
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>商品管理</title>
</head>
<body>
<script type="module" src="/src/main.ts"></script>
</body>
</html>lang="zh-CN" 声明页面语言,charset="UTF-8" 指定字符编码,title 设置浏览器标签页名称。viewport 让移动端按设备宽度显示。
script 指向刚修改的 src/main.ts;type="module" 使脚本按模块方式加载,可以使用 import。这个脚本通过 Vite 开发服务处理后才交给浏览器,不是要求浏览器直接理解 TypeScript 类型语法。
body 中没有手写标题和段落,因为它们由 main.ts 创建。模板里原有的空容器和图标引用在这份入口中已经不再使用。
替换模板样式
将 src/style.css 的全部内容替换为:
* {
box-sizing: border-box;
}
body {
margin: 0;
background: #f5f6f8;
color: #1f2937;
font-family: Arial, "Microsoft YaHei", sans-serif;
line-height: 1.6;
}
.page {
max-width: 720px;
margin: 40px auto;
padding: 24px;
background: #fff;
}
.page h1 {
margin: 0 0 16px;
font-size: 24px;
}
.page p {
margin: 0;
}body 设置整页背景、文字与字体;.page 对应 main.ts 中的类名,在浅灰背景上显示白色内容区,限制宽度并居中。padding 留出内部间距,margin 留出外部间距。其余选择器分别调整标题和段落。
三个文件都保存后,浏览器标签页标题为“商品管理”,页面显示:
商品管理
矿泉水模板中的 src/counter.ts、src/assets/ 和 public/ 资源本节不需要使用,可以先保留;新的 main.ts 已经不再导入计数器和标识图片。
单独运行类型检查
明确开启严格检查
tsconfig.json 是 TypeScript 的配置文件。TypeScript 的检查与编译工具 tsc 读取它,决定检查哪些文件、采用哪些类型检查规则。配置文件的作用见 TypeScript 官方中文文档:tsconfig.json 是什么。
打开项目根目录的 tsconfig.json,找到 "compilerOptions": {,在它下一行加入 "strict": true,,其余模板配置保留原样。如果已经存在 strict,就将它的值设为 true,不重复添加同名选项。
strict 开启一组更严格的类型检查。这里先固定项目的检查方式,不逐项修改模板配置;配置的详细含义在用到对应问题时再说明。
检查当前代码
保留运行 Vite 的终端,通过 Terminal > New Terminal 新建第二个命令提示符终端,确认当前目录仍为 ts-demo。执行:
npm run check它会运行 package.json 中的 tsc --noEmit,其中 --noEmit 表示只检查、不生成输出文件。
当前代码通过时,终端显示脚本名称和执行命令,然后回到可输入命令的提示符,不额外输出“检查成功”:
> ts-demo@0.0.0 check
> tsc --noEmit这次检查读取 tsconfig.json 中配置的源码范围,不只检查编辑器当前打开的文件。
对照一次类型错误
在 src/main.ts 中,只把定义 productName 的这一行改为下面的内容,其余代码不动并保存:
const productName: string = 100数字 100 不符合 string 的约定。再次在第二个终端执行 npm run check,本节环境得到的核心错误为:
error TS2322: Type 'number' is not assignable to type 'string'.终端同时给出 src/main.ts 的行列位置;不同版本的显示排版可能有差异。VS Code 的 TypeScript 提示通常也会给这一行加红色波浪线,鼠标悬停可以查看原因。
此时浏览器显示 100,页面没有类型错误覆盖层。刷新页面后也仍能运行:Vite 的开发服务将项目源码处理成浏览器可以执行的 JavaScript,不代替刚才的类型检查;处理后的代码不再包含类型标注,因此不会在运行时按 string 的约定检查这个值。这里的数值在赋给 textContent 时被浏览器转成了文字,所以看到了 100。
如果页面没有更新,先确认开发服务仍在运行、文件已经保存,再刷新页面。
因此,页面能打开不能证明类型检查已经通过,红线也不是浏览器替程序拒绝了这个值。类型检查与生成 JavaScript 的区别见 TypeScript 官方中文手册:基础;Vite 的处理方式见 Vite 官方中文文档:TypeScript。
核对后,把这一行恢复为:
const productName: string = '矿泉水'保存,再执行 npm run check,确认检查通过,页面恢复“矿泉水”。不通过时先检查终端给出的文件位置,不要改掉类型标注来绕过这次错误。
启动或显示不符合预期时
npm 提示脚本被禁止运行
这通常表示当前打开的仍是 PowerShell。按开头的步骤将默认终端选为 Command Prompt,再新建终端,在 ts-demo 根目录重试 npm 命令。已有终端不会因更改默认选项自动变成命令提示符。
提示找不到 package.json 或脚本
先看终端当前目录,应该是项目根目录 ts-demo,不是上一级 Course,也不是 src。通过 VS Code 打开正确文件夹并新建终端,再检查 package.json 的 scripts 中有 dev 和 check。首次安装失败时,先恢复网络并重新执行 npm install。
仍然显示模板或旧内容
确认改的是当前项目的 src/main.ts 和 src/style.css,文件已经保存,浏览器地址与运行终端的 Local 地址一致。存在多个开发服务时,不要仅凭端口习惯判断打开了哪一个项目;仍有疑问时刷新页面。
有商品标题,却看不到商品名称
核对 productNameElement.textContent = productName 和 page.append(title, productNameElement) 是否都保留。前者写入文字,后者把段落放到主体中;只创建元素不会自动显示。
连续核对项目运行
- 确认
main.ts中的 productName 为字符串“矿泉水”,执行npm run check,没有类型错误。 - 保持开发服务运行,把这一行的名称改为“可乐”并保存。不手动刷新,确认页面更新为“可乐”,再恢复“矿泉水”。
- 在运行 Vite 的终端按
Ctrl+C停止服务;Windows 若询问是否终止批处理任务,输入Y并回车。刷新浏览器,确认原服务地址已经不能加载页面。 - 在该终端重新执行
npm run dev,按终端显示的 Local 地址打开,确认“商品管理”和“矿泉水”恢复显示。 - 再执行一次
npm run check,确认检查通过,并保留当前项目供下一节使用。