有時候 TS 不需要你明確寫型別,它自己就能猜出來。 這就是型別推論(Type Inference)。 昨天我們聊了 Enum,今天我們來看看 TS 的「神之直覺」—— 型別推論。 簡單來說,就是你不寫型別,TS 也能猜出來你要幹嘛(大部分時候很準)。
TypeScript 會在變數宣告的那一刻,就根據賦值內容自動決定型別。
let age = 18; // 推論成 number
let name = "Marco"; // 推論成 string
let isActive = true;// 推論成 boolean
這時候就算你沒寫 : number
,TS 也不會讓你隨便改型別:
age = "十八";
// ❌ Type 'string' is not assignable to type 'number'
TS 也能看陣列的內容來推論型別:
let scores = [100, 90, 80];
// 推論成 number[]
如果混合型別:
let mixed = [1, "two", 3];
// 推論成 (string | number)[]
函式沒寫回傳型別時,TS 會自動根據 return
推論:
function add(a: number, b: number) {
return a + b; // 推論回傳 number
}
沒有 return
的話,就會推論成 void
:
function logMessage(msg: string) {
console.log(msg); // 推論回傳 void
}
TS 會自動把值推論成比較「寬」的型別:
let direction = "up";
// 推論成 string(任何字串都能代替)
const strictDirection = "up";
// 推論成字面量 "up"(只能是這個值)
想要「鎖死值」:
const
as const
:let colors = ["red", "green", "blue"] as const;
// 推論成 readonly ["red", "green", "blue"]
在物件解構時,TS 也會自動幫你帶上型別:
const user = { name: "Alice", age: 20 };
const { name, age } = user;
// name -> string
// age -> number
陣列解構也可以:
const arr = [1, "hi"] as const;
const [id, greeting] = arr;
// id -> 1
// greeting -> "hi"
this
的推論TS 甚至能推論 this
:
const counter = {
value: 0,
increment() {
this.value++;
}
};
在這裡,this
自動被推論為:
{ value: number; increment: () => void; }
let items = [];
// 推論成 any[](什麼都能放,非常危險)
items.push(123);
items.push("abc"); // 也 OK
解法:
明確標註型別
let items: number[] = [];
null
/ undefined
function getUser() {
if (Math.random() > 0.5) {
return { name: "Alice" };
}
return null;
}
// 推論成 { name: string } | null
這種情況就要處理 null
,不然會報錯:
const user = getUser();
if (user) {
console.log(user.name);
}
function getValue(flag: boolean) {
return flag ? 1 : "one";
}
// 推論成 number | string
這在程式裡會傳染,讓型別變得很麻煩。
建議:用明確的回傳型別。
有時候 TS 不只是看值本身,還會「反推」你使用的情境。
window.addEventListener("click", (event) => {
console.log(event.clientX);
// event 自動推論成 MouseEvent
});
這叫做 上下文型別 (Contextual Typing),讓我們不用再寫一堆型別。
any
型別推論就像 TS 的「第六感」:
👉 最安全的做法:
能省則省,但關鍵地方要明寫。
type User = {
id: number;
name: string;
isActive: boolean;
};
function createUser(id: number, name: string, isActive: boolean): User {
return { id: id, name: name, isActive: isActive };
}
const user: User = createUser(1, "Alice", true);
這樣寫很嚴謹,但型別幾乎重複寫了兩次,顯得很囉嗦。
type User = {
id: number;
name: string;
isActive: boolean;
};
function createUser(id: number, name: string, isActive: boolean) {
return { id, name, isActive };
// 🔑 這裡 TS 會自動推論回傳型別為 User
}
const user = createUser(1, "Alice", true);
// user -> { id: number; name: string; isActive: boolean }
這樣寫:
不過如果這個 createUser
是 API 對外匯出的函式,建議還是加上明確回傳型別,避免未來改動時「默默壞掉」:
function createUser(id: number, name: string, isActive: boolean): User {
return { id, name, isActive };
}
👉 內部工具函式:可以放心用推論
👉 對外公開 API:最好明寫型別