← FC Coder · HomePhase 02 · Lesson 20 · 60 min
Lesson20
Phase Two · Fantasy Builder · Matchday 20 · Add the Rules

TS interface Player
给数据加角色

Today's 3 Jobs · 今天这三件事
  1. 01
    新建 lib/types.ts 写 export interface Player
    5 字段球员档案模板
  2. 02
    PlayerCard / sandbox 用 Player 类型
    : Player 注解
  3. 03
    🌟 Cursor 弹自动补全 + 拼错红线
    代码会自己说话

Phase 0-19 你用的是 JS · 宽松 · 错了运行时才发现。今天 TypeScript 进场 —— JS + 类型注解。`interface Player` = 球员档案的模板 · 5 字段必须有 · Cursor 看到 player 自动列字段 · 拼错立刻红线。代码会自己说话

Essence · 顺嘴本质点

类型 = 数据的角色

Phase 1 #13 player 是档案 · 今天 Player 是档案的模板。

Chalk Board · interface Player
export interface Player {
name: string;
rating: number;
position: string;
nationality: string;
club: string;
}
5 字段
name / rating / position / nationality / club
类型
string / number
({ player }: { player: Player })

import type 只导入类型 · 不打包到运行时。现代 TS 共识。

Essence · TS 给的礼物

自动补全 + 拼错抓得到

player. 弹 5 字段 · player.fame 红线 · 编辑器层面就抓。

Chalk Board · JS vs TS
player. → name / rating / position / nationality / club
player.fame → ⚠️ Property 'fame' does not exist
JS · 宽松
写啥都能跑 · 拼错运行时才发现
TS · 加规矩
Cursor 弹候选 · 拼错立刻红线
成本
多写几个 : 类型注解

Cursor IntelliSense 给的"自动补全"力量来自 TS,不是 LLM。这个用 OK。

Roster · 今天 3 个新工具

interface · 注解 · import type

抄着用 · 看 Cursor 自动补全是今天的「哇」。

  1. 01
    interface Player { name: string; ... }
    数据角色模板 —— 任何 Player 必须 5 字段 · 类型正确。
    export 给全项目用。首字母大写约定。
  2. 02
    ({ player }: { player: Player })
    类型注解 —— 告诉 Cursor 参数是什么类型。
    注解后 · Cursor 看 player.* 自动列字段。
  3. 03
    import type { Player } from '@/lib/types'
    type-only 导入 —— 只导入类型 · 不打包到运行时。
    现代 TS 共识 · 性能稍好。抄着用。
Half 2 · 在屏幕上

造类型 · 用类型 · 看 Cursor 变聪明

做完一步就点 ✓。Step 6 弹补全那一下是今天的「哇」。

01造类型 · lib/types.ts
01Min

Cursor + pnpm dev

老三件套。
02Min

lib/ 右键 New File · types.ts

新文件 · 空白。
03Min

写 export interface Player { name: string; rating: number; ... }

5 字段 · 每个加类型。name: string / rating: number / position: string / nationality: string / club: string。保存。
02PlayerCard / sandbox 用 Player
04Min

PlayerCard 顶部 import type { Player } · 删 PlayerLike

import type { Player } from '@/lib/types'; 然后 function PlayerCard({ player }: { player: Player }) { ... }。保存。
05Min

回 sandbox · 22 张卡正常 · 无报错

Cursor 看 .map(p => <PlayerCard player={p} />) 时知道 p 是 Player。
03🌟 Cursor 弹补全 + 抓拼错
06Min

🌟 sandbox 写 const haaland = players[0]; haaland.

停在 . 后面 · Cursor 弹 5 个候选 name / rating / position / nationality / club。这是 TS 给的力量。
07Min

故意写 haaland.fame · 看 Cursor 红线

Property 'fame' does not exist on type 'Player'. 编辑器抓错 · 不用跑代码。
08Min

鼠标停在 haaland 看类型推断

弹小框 · 显示 const haaland: Player · TS 自动推断。
04玩 · 加可选字段 · 截图
09Min

新建 lib/players.ts · 强类型 players + byRating

import playersData from ...; import type { Player } from './types'; export const players: Player[] = playersData as Player[]; export function byRating(min: number): Player[] { return players.filter(p => p.rating >= min); }
10Min

sandbox import { players, byRating } from '@/lib/players'

代替之前的 import players from '@/app/data/players.json'。现在每个 player 都是 Player 类型 · 字段补全更准。
11Min

试加可选字段 face?: string

lib/types.ts: interface Player { ... face?: string } · ? 表示可选。Cursor 仍允许 player.face 但提示「可能 undefined」。今天感受一下。
12Min

📸 截图战利品

Mac Shift + Cmd + 4 框 Cursor 弹补全那一刻。存 2026-XX-XX-我的第一个interface.png · Phase 2 第五张战利品。
Half Time · 中场 · 讲给爸爸听

4 题 · 重点:TS 比 JS 早抓错

第 4 题让他讲为什么早抓比晚抓好。

01什么是 TypeScript?和 JS 差在哪?用「宽松 vs 加规矩」讲。Hint ↓

TS = JS + 类型注解。编辑器层面就抓错 · 不用等运行。

02interface Player { name: string; ... } 是什么?用「档案模板」比喻。Hint ↓

任何 Player 角色 · 必须 5 字段 · 类型正确。Phase 1 #13 player 是档案 · 今天 Player 是档案的模板。

03Cursor 看到 player. 弹 5 个候选 —— 为什么 TS 比 JS 准?Hint ↓

TS 知道 player 类型,知道有哪些字段。JS 啥都不知道,只能猜。

04player.fame 拼错时 TS 立刻红线。JS 时代什么时候发现?Hint ↓

JS 要等运行才发现(undefined → 卡上空白 / NaN)。TS 编辑时就抓。早抓比晚抓好 10 倍。

Player Rating · 本课温度计

给「Cursor 变聪明」打个分

说真话。爸爸会看到。

今天难度Difficulty
0
今天开心Fun
0
Final Whistle · 终场哨

你的代码有规矩了 —— Cursor 变成了聪明的同事。

还有 12 步没打勾。Step 6 弹补全那一下今天就过 —— 其他下次补。