05 — TypeScript 快速入门

30 分钟掌握 React 项目中用到的 TypeScript 语法。
TypeScript = JavaScript + 类型系统。代码本身还是 JS,只是多了「类型标注」。


1. TypeScript 是什么?

1
2
3
4
5
6
7
8
9
10
11
// JavaScript(运行时才检查错误)
function add(a, b) {
return a + b;
}
add("5", 3) // "53"(字符串拼接,不会报错,但结果不是你想要的)

// TypeScript(写代码时就报错)
function add(a: number, b: number): number {
return a + b;
}
add("5", 3) // ❌ 报错:Argument of type 'string' is not assignable to parameter of type 'number'

核心思想:在写代码时(编译阶段)就能发现错误,IDE 还能智能提示。
编译过程*.ts / *.tsxtsc 编译 → *.js(浏览器只认识 JS)。


2. 项目里用到的所有 TS 语法(速查表)

下面是项目中实际出现的 TS 语法,挑你不会的看:

2.1 基础类型

1
2
3
4
5
6
7
8
9
10
11
12
let str: string = 'hello';
let num: number = 42;
let bool: boolean = true;
let nothing: null = null;
let notDefined: undefined = undefined;

// 数组
let arr: number[] = [1, 2, 3];
let arr2: Array<string> = ['a', 'b'];

// 对象
let obj: { name: string; age: number } = { name: 'Alice', age: 30 };

2.2 联合类型(Union Type)

项目里到处都是

1
2
3
4
5
6
7
8
let value: string | null = null;          // 字符串或 null
let flag: 'success' | 'error' = 'success'; // 字面量联合

// 角色
type Role = 'user' | 'assistant';

// Agent ID
type AgentId = 'chat' | 'file' | 'ppt' | 'deep' | 'skills';

项目例子

1
2
3
4
5
6
7
8
9
10
11
// types/api.ts
code: number; // 单个类型
message: string;
data: T; // ← 泛型,下面讲

// constants/agents.ts
type AgentId = 'chat' | 'file' | 'ppt' | 'deep' | 'skills';
// ↑ 只能是这五个值之一,其他都报错

// chatStore.ts
role: 'user' | 'assistant'; // ChatMessage 的 role 字段只能是这两个值

2.3 数组类型

1
2
3
4
5
6
7
8
let list: string[] = ['a', 'b', 'c'];
let list2: Array<string> = ['a', 'b', 'c']; // 泛型写法(效果一样)

// 项目里
chatList: Chat[];
messages: ChatMessage[];
agents: Agent[];
timeline: TimelineItem[];

2.4 可选字段 ?

1
2
3
4
5
6
7
8
interface User {
name: string; // 必填
age?: number; // 可选(可以没有)
}

const u1: User = { name: 'A' }; // OK
const u2: User = { name: 'B', age: 30 }; // OK
const u3: User = { age: 30 }; // ❌ 缺 name

项目里

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// chatStore.ts 的 ChatMessage
interface ChatMessage {
id: string;
role: 'user' | 'assistant';
content: string;
thinking?: string[]; // ← 可选
timeline?: TimelineItem[]; // ← 可选
reference?: ReferenceItem[]; // ← 可选
recommend?: string[]; // ← 可选
file?: boolean;
fileName?: string | null;
showTimeline?: boolean;
copied?: boolean;
pptFile?: string;
timestamp: number;
}

读字段时要用 ?.if 判断:

1
2
3
4
{message.timeline && message.timeline.length > 0 && (
<Timeline items={message.timeline} open={!!message.showTimeline} ... />
)}
// ↑ 先判断是否 undefined,再取 .length

2.5 接口 interface — 项目最常用

1
2
3
4
5
6
7
8
9
10
// 定义对象的形状
interface Props {
chat: Chat;
active: boolean;
onSelect: (id: string) => void; // 函数类型
onDelete?: (id: string) => void; // 可选函数
}

// 使用
function ChatItem({ chat, active, onSelect, onDelete }: Props) { ... }

2.6 type 别名

1
2
3
4
5
6
// type 和 interface 很像,但更灵活
type AgentId = 'chat' | 'file' | 'ppt' | 'deep' | 'skills';

type Callback = (id: string) => void;

type Status = 'running' | 'completed';

什么时候用 interface,什么时候用 type

场景推荐
描述对象形状interface
联合类型 / 字面量type
函数类型type(更简洁)
需要扩展(extends)interfacetype

项目里两种都用

2.7 泛型 <T> — 项目里有几个

1
2
3
4
5
6
7
8
// 函数定义
function identity<T>(arg: T): T {
return arg;
}

// 调用
identity<string>('hello'); // T 被推断为 string
identity(42); // T 被推断为 number

项目里的例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// api/client.ts
async function parseOrThrow<T>(res: Response): Promise<T> {
// T 是任意类型,调用时由外部指定
...
return body.data as T;
}

async function httpGet<T>(path: string): Promise<T> {
// T 是返回值类型
...
}

// 使用时指定
const data = await httpGet<{ records: SessionListVO[] }>('/session/list');
// ↑ 告诉 TS 返回的是 { records: ... }

2.8 函数类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 完整写法
type OnSelect = (id: string) => void;

// 内联写法
interface Props {
onSelect: (id: string) => void;
}

// 异步函数
type AsyncFn = (id: string) => Promise<void>;

// 项目里
onSelect: (id: string) => void;
onDelete?: (id: string) => void;
toggleTimeline: (id: string) => void;

2.9 类型断言 as!

1
2
3
4
5
6
// 1. `as` 强制告诉 TS 某个值的类型
const data = body as MyType;

// 2. `!` 告诉 TS 某个值不会是 null/undefined
const root = document.getElementById('root')!;
// ↑ 实际是 HTMLElement | null,加 ! 表示一定是 HTMLElement

项目里

1
2
3
4
5
6
7
8
9
// main.tsx
ReactDOM.createRoot(document.getElementById('root')!).render(
// ↑ 我知道 root 一定存在
<React.StrictMode><App /></React.StrictMode>
);

// api/client.ts
const body = (await res.json()) as BaseResult<T>;
// ↑ 告诉 TS 返回的是这个形状

⚠️ 慎用 as,应该让 TS 自动推断。

2.10 联合类型 + 类型守卫

1
2
3
4
5
6
7
8
9
10
type Status = 'running' | 'completed';
type Item = { type: 'thinking'; content: string } | { type: 'tool'; toolName: string };

function handle(item: Item) {
if (item.type === 'thinking') {
console.log(item.content); // TS 知道这里有 content
} else {
console.log(item.toolName); // TS 知道这里有 toolName
}
}

项目里

1
2
3
4
5
6
7
8
9
10
11
12
// chatStore.ts 的 TimelineItem
type TimelineItem =
| { type: 'thinking'; content: string }
| { type: 'tool'; toolName: string; status: 'running' | 'completed' }
| { type: 'error'; message: string };

// Timeline.tsx 中
if (item.type === 'thinking') {
// item.content 可用
} else if (item.type === 'tool') {
// item.toolName、item.status 可用
}

3. 项目的类型层次(怎么看)

项目里类型分三层:

1
2
3
types/api.ts                ← 后端接口的原始数据类型
store/chatStore.ts ← 前端用的领域类型(Chat, ChatMessage...)
components/Xxx/Xxx.tsx ← 组件 Props 类型(紧邻组件定义)

例子:消息类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// types/api.ts — 后端原始数据
interface MessageVO {
id: number;
question: string | null;
answer: string | null;
thinking: string | null;
// ...
}

// store/chatStore.ts — 前端领域类型
interface ChatMessage {
id: string;
role: 'user' | 'assistant';
content: string;
timeline?: TimelineItem[];
// ...
}

// 在 Store 中转换
function mapSessionDetail(detail: SessionDetailVO): ChatMessage[] {
// MessageVO[] → ChatMessage[]
}

4. 完整例子:从接口定义到组件使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// 1. 定义后端数据类型(types/api.ts)
interface SessionListVO {
conversationId: string;
question: string | null;
}

// 2. 定义前端领域类型(store/chatStore.ts)
interface Chat {
id: string;
title: string;
messages: ChatMessage[];
isNew: boolean;
}

// 3. 定义组件 Props(ChatItem.tsx)
interface Props {
chat: Chat; // 完整对象
active: boolean;
onSelect: (id: string) => void;
}

// 4. 使用
function ChatItem({ chat, active, onSelect }: Props) {
return (
<div onClick={() => onSelect(chat.id)}>
{chat.title}
</div>
);
}

// 5. 调用
<ChatItem chat={someChat} active={true} onSelect={handleSelect} />

5. 常见 TS 报错与解决

报错原因解决
Type 'string' is not assignable to type 'number'类型不匹配改类型或改值
Object is possibly 'undefined'可能为 undefined?.if 判断
Property 'xxx' does not exist on type 'Yyy'对象上没有该字段检查拼写或加可选 ?
Argument of type '...' is not assignable to parameter of type '...'函数参数类型不对检查函数签名

6. 项目 TypeScript 配置(看个大概)

1
2
3
4
5
6
7
8
9
10
11
12
// tsconfig.json(节选)
{
"compilerOptions": {
"target": "ES2020",
"lib": ["ES2020", "DOM"], // DOM 提供 document 等浏览器 API 类型
"jsx": "react-jsx", // 让 <div> 被识别为 JSX
"strict": true, // 严格模式(推荐开启)
"paths": {
"@/*": ["./src/*"] // @ 别名指向 src
}
}
}

关键配置

  • strict: true:开启所有严格检查(不要关闭)
  • jsx: "react-jsx":启用 JSX 支持
  • paths: { "@/*": ["./src/*"] }:启用 @/ 路径别名

7. 一段话总结

TypeScript 给 JS 加了「类型」:标注变量类型、函数参数/返回值类型、对象形状。
项目里 80% 的 TS 语法就是:

  1. interface 定义对象/Props 形状
  2. type 定义联合类型
  3. 可选字段 ? 表示可能没有
  4. 泛型 <T> 出现在 httpGet<T>useState<T>useRef<T>

掌握这四点就能读懂 95% 的项目代码。


接下来

现在你已经熟悉 React 项目所需的全部基础(HTML/CSS/JS/TS)。
继续阅读 06-前端工具链npm与Vite.md,理解 Vite、npm、import 解析等工具链概念。