由於許多人應該是沒有寫過 TypeScript
於是這邊會簡單介紹 TypeScript 的基礎
如果想知道更深可以參照官網
在剛開始學習 TypeScript 的過程中
一定會想說從官方的範例開始照著實作
後來發現這是夢魘的開始啊~~~
TypeScript 的官方範例非常的完(艱)整(澀)
所以比起啃那些文件,多數人應該是像我一樣 Stack Overflow 學習法
副檔名
TypeScript 有兩種檔案:.ts
、.d.ts
.ts
副檔名的檔案就是 TypeScript 檔案(廢話)
.d.ts
副檔名則是代表型別的定義檔案
一般來說,我們會將自定義的型別放到 .d.ts
裡面
除了這兩種檔案之外,開發 React 時,會習慣使用 .tsx
來撰寫
如同 .jsx
一般,讓開發時可以更清楚了解這個檔案是 React 的元件
或許有些開發者會覺得不需要區分 .js
/ .jsx
但對於開發上的溝通來說,還是建議標示比較合適
型別定義
我們之所以喜歡 TypeScript,在於它可以良好地定義我們想要的型別:
type User = {
name: string
age: number
picture: string | null
role: "admin" | "member" | "guest"
description?: string
}
上面的自訂型別說明了:
- 有一個名為 User 的型別
- User 型別裡面有 name, age, picture, description 屬性
- picture 可為 string 或是 null
- role 只可為 "admin", "member" 或是 "guest"
- description 可以沒有(undefined)
於是我們就可以像這樣子使用:
const addUser = (user: User) => {
console.log(user.name)
console.log(user.picture) // 可能為 null
console.log(user.role) // 只可能為 admin, member, guest
console.log(user.description) // 可能為 undefined
console.log(user.foo) // 會編譯失敗
}
有了上面的自訂型別後,我們就可以避免惱人的 TypeError: Cannot call method 'xxx' of undefined
,更嚴謹與優美地撰寫我們的程式
這邊刻意不提到 interface
,建議直接使用 type
來定義自訂型別即可(用更 functional 思維)
可參考:interface v.s. type
至於 enum
的話,在一般情況我們是不需要使用的,直接使用字串即可,例如:
type Currency = "NTD" | "USD"
只有在一種情形下建議使用 enum
,那就是我們需要使用 reverse mapping
也就是有 key 跟 value,且需要從 value 對應回 key 的情況,舉例來說:
enum Reward {
Poor = 1,
Soso = 3,
Good = 5,
}
Reward.Good -> 5
Reward[3] -> "Soso"
不過還是建議減少 reverse mapping 的用法,主要是會讓程式碼的可讀性下降不少
到目前為止,許多套件還是以 type
使用為主
React 中實際使用
相信大家最卡關的應該就是這個部分
使用在 React 裡面時到底該如何定義型別?
我們這邊以各情況來進行說明
定義 Functional Component
type ButtonProps = {
text: string
}
const Button: React.FC<ButtonProps> = ({ text }) => {
return <button>{text}</button>
}
export default Button
首先需要先定義 Props,再來定義元件
元件請使用 React 定義好的 FunctionalComponent 型別(簡寫 FC)
在 TypeScript 中的 <>
相當於 function 中的 ()
我們稱為型別參數,可以想成是 function 中的參數
所以 React.FC<ButtonProps>
代表定義一個 Props 為 ButtonProps 的 FC
定義 Event Handler
const App = () => {
const handleClick: React.MouseEventHandler<HTMLButtonElement> = e => {
console.log(e)
}
return <button onClick={handleClick}>Click</button>
}
在 TypeScript in React 中最難的就是 EventHandler 的定義
這邊拆成兩部分解釋:
- Event Handler
我們必須先選擇這是要處理哪種 Event 的 Handler,Event 種類有很多,都是以 Event 結尾,像是 MouseEvent、FormEvent、KeyboardEvent 等等,可參考 React 官方文件 - Target Element
接下來我們必須定義 Handler 的作用目標是什麼,以範例來說,我們要處理的是作用在「按鈕」上面的點擊事件,所以我們必需要使用HTMLButtonElement
來宣告我們的作用目標,作用目標有很多種,都是 HTML 開頭,Element 結尾,像是 HTMLFormElement, HTMLDivElement 等等,可參考 MDN
這邊建議使用
const handler: EventHandler = e => {}
而不是
const handler = (e: Event) => {}
主要是因為我們盡量使用 React 已經幫我們定義好的型別,避免自己重複定義 Handler 型別
定義共用型別
type UserRole = "admin" | "member" | "guest"
像這種型別常常會跨元件共用,這邊建議放到 src/types
底下
並且依照功能區分,例如:
跟使用者相關的放到 src/types/user.d.ts
跟付款相關的放到 src/types/payment.d.ts
這邊記得要使用 .d.ts
作為副檔名,這樣在編譯時才會自動將型別放進去
PS. create-react-app v3 是使用 TypeScript 3.7,有提供 optional chaining 的語法,具體用法如下:
// TypeScript 3.7 以前
foo && foo.bar && foo.bar.baz
// TypeScript 3.7 以後
foo?.bar?.baz
結語
型別定義的部分其實有更多部分可以講
但是如果全部打完的話,這篇大概就是我的最後一篇了
所以之後會以實際案例來介紹,希望這篇能稍微幫上點忙
murmur...
還有四天,快過半了!!!