iT邦幫忙

2026 iThome 鐵人賽

DAY 18
0
自我挑戰組

程式碼 AI 寫,我負責看懂:30 天拆解 React × TypeScript系列 第 18

Day 18|useState<User> 裡面的 <User>

  • 分享至 

  • xImage
  •  

State 後的尖括號

前面認識 useState() 時,看過這種寫法:

const [text, setText] = useState('')

不過當 State 要保存使用者資料時,可能會看到:

const [user, setUser] = useState<User | null>(null)

useState 後面多出來的 <User | null>,不是 JSX,也不是一個叫作 User 的 Component,而是 TypeScript 的 Generic,也就是「泛型」寫法。

目前可以先把它理解成:告訴 useState(),這份 State 準備存放什麼型別的資料。

先看看 User 是什麼

在使用 User 之前,需要先定義它的資料格式:

interface User {
  id: number
  name: string
}

這表示一份 User 資料必須包含數字型別的 id,以及字串型別的 name

把它交給 useState() 之後:

const [user, setUser] = useState<User | null>(null)

user 就可能是「一份符合 User 格式的資料」,也可能是 null,代表目前還沒有使用者資料。

這樣之後不管是:

setUser({ id: 1, name: '小明' })

還是:

setUser(null)

都符合這個型別。但如果傳進去的物件少了 name,TypeScript 就會提醒缺少這個屬性。

泛型

useState() 可以保存很多不同種類的資料,字串、數字、布林值,甚至是 User 物件都可以。
如果 useState() 只能綁定某一種固定型別,它就很難在不同情況下重複使用。

泛型讓 useState() 可以先保留一個型別的位置,再由使用它的人決定這次要放入什麼型別。

可以把它想成一個標示內容物的盒子:

  • useState<string>:放字串
  • useState<number>:放數字
  • useState<User | null>:放 Usernull

所以 <User> 並不是建立一份 User 資料,而是在指定這次 useState() 使用的型別。
TypeScript 官方文件:Generics

每次使用 useState() 都要寫尖括號嗎?

不一定。TypeScript 具有型別推論能力,可以根據初始值判斷 State 的型別。
例如 useState('') 的初始值是字串,TypeScript 就能推論出 string;同樣 useState(0)useState(false) 也能分別推論出 numberboolean

型別很明確時就不需要特地寫成:

useState<string>('')

為什麼初始值是 null 時要寫型別?

如果只寫成:

useState(null)

TypeScript 看到的初始值只有 null,可能會把這份 State 推論成只能存放 null。之後想放入 User 資料時,就會被 TypeScript 標示型別錯誤。

因此需要明確告訴 TypeScript,這份 State 不只可以是 null,之後也可以放入 User

const [user, setUser] = useState<User | null>(null)

這也是 <User | null> 常見的使用情境:初始狀態還沒有資料,但之後會放入一份物件。
React 官方文件:在 React 中使用 TypeScript

useState<Todo[]>([])

const [todos, setTodos] = useState<Todo[]>([])

Todo[] 表示這是一個只能放入 Todo 資料的陣列,[] 則是 State 的初始值,代表一開始還沒有任何 Todo。

如果只寫:

useState([])

空陣列裡沒有任何資料,TypeScript 很難從內容判斷之後要放入哪種型別的項目,所以專案才明確寫上:

useState<Todo[]>([])

這樣之後呼叫 setTodos() 時,TypeScript 就會檢查陣列中的每個項目是否符合 Todo 的格式。

<User> 和 JSX 的標籤不一樣

第一次看到 <User>,很容易聯想到 React Component 的 <User />,但兩者的位置與用途不同。

寫在函式名稱後面的:

useState<User>

其中的 <User> 是 TypeScript 的型別資訊,用來指定這份 State 保存的資料型別。

寫成:

<User />

則是 JSX,表示要使用名為 User 的 Component。
只要注意它出現的位置,就能分辨這兩種不同的用途。

今日回顧

  • useState<User> 裡的 <User> 是 TypeScript 的泛型寫法,用來告訴 useState() 這份 State 要保存什麼型別
  • <User> 不會建立 User 資料,也不是 JSX Component
  • 初始值型別很明確時,TypeScript 通常可以自動推論,不一定要手動寫泛型
  • 初始值是 null 時,可以使用 User | null 表示之後也能放入 User
  • useState<Todo[]>([]) 表示建立一份初始值為空陣列的 Todo State

useState() 後面的尖括號,是在提前告訴 TypeScript:「這份 State 準備保存什麼型別的資料。」
所以即使初始值只有 null 或空陣列,TypeScript 也能知道之後可以放入 UserTodo 資料。
下篇見~


上一篇
Day 17|string、number 我懂,但跑出一個 string | null 就不懂了
下一篇
Day 19|Props 也要管型別?第一次讓 TypeScript 檢查 Component
系列文
程式碼 AI 寫,我負責看懂:30 天拆解 React × TypeScript22
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言