iT邦幫忙

1

TypeScript - 工具型別 Utility Types

  • 分享至 

  • xImage
  •  

寫專案時一定會遇到這種情況:
同一個「使用者」,在不同地方需要的欄位不太一樣:

  • 註冊時:全部都要填。
  • 修改個人資料時:只改有變的欄位就好。
  • 登入時:只需要 email 跟密碼。
  • 顯示公開頁面時:絕對不能把密碼傳出去。

如果每種情況都重寫一個 type,欄位一多、一改就會改到漏掉。所以 TS 內建了一組工具型別,讓你從一個「主型別」直接生出其他型別,今天就來聊聊關於工具型別 Utility Types。

以下範例都用這個 User:

type User = {
  id: number;
  name: string;
  email: string;
  password: string;
  age?: number;
};

Partial — 全部變成可選

Partial<User> 會把 User 的每個欄位都加上 ?。最典型的用法就是「修改資料的表單 / PATCH API」:使用者只改了名字,就只送名字。

function updateUser(id: number, data: Partial<User>) {
  console.log(`更新使用者 ${id}`, data);
}

updateUser(1, { name: '小華' });                 // ✅ 只改名字
updateUser(1, { age: 19, email: 'b@birc.tw' });  // ✅ 改兩個
updateUser(1, { age: '十九' });
// ❌ Type 'string' is not assignable to type 'number'.

欄位都變成可選了,但型別檢查跟拼字檢查都還在,這就是比直接寫 any 好的地方。

Required — 全部變成必填

Required 跟 Partial 相反,把所有 ? 拿掉。例如註冊流程走完之後,age 一定要有:

type CompleteUser = Required<User>;

const cu: CompleteUser = { id: 1, name: '小明', email: 'a@birc.tw', password: '1234' };
// ❌ Property 'age' is missing in type '{ id: number; name: string; email: string; password: string; }' but required in type 'Required<User>'.

Pick — 只挑某幾個欄位

Pick<User, 'email' | 'password'> 會從 User 裡挑出指定的欄位,要挑的欄位用第四章的 Literal + Union 來寫。適合「登入表單」這種只需填寫一小部分欄位的情況:

type LoginForm = Pick<User, 'email' | 'password'>;
// 等同於 { email: string; password: string }

const form: LoginForm = { email: 'a@birc.tw', password: '1234' }; // ✅
const form2: LoginForm = { email: 'a@birc.tw' };
// ❌ Property 'password' is missing in type '{ email: string; }' but required in type 'LoginForm'.

Omit — 去掉某幾個欄位

Omit 剛好跟 Pick 相反:全部留下,除了指定的欄位。最重要的用法就是「公開資料不能有密碼」:

type PublicUser = Omit<User, 'password'>;
// 等同於 { id: number; name: string; email: string; age?: number }

function toPublic(user: User): PublicUser {
  const { password, ...rest } = user; // 把 password 拆出來丟掉
  return rest;
}

const pu = toPublic({ id: 1, name: '小明', email: 'a@birc.tw', password: '1234' });
console.log(pu.password);
// ❌ Property 'password' does not exist on type 'PublicUser'.

如果 API 回傳的型別寫成 PublicUser,就算哪天有人不小心想把密碼塞進去,TS 也會擋下來:

const leak: PublicUser = { id: 1, name: '小明', email: 'a@birc.tw', password: '1234' };
// ❌ Object literal may only specify known properties, and 'password' does not exist in type 'PublicUser'.

Readonly — 全部變成唯讀

Readonly<User> 就是幫每個欄位都加上本章一開始講的 readonly。適合「從 API 拿到、只拿來顯示、不該被改」的資料:

const me: Readonly<User> = { id: 1, name: '小明', email: 'a@birc.tw', password: '1234' };

me.name = '小華';
// ❌ Cannot assign to 'name' because it is a read-only property.

組合起來用

工具型別可以疊在一起。例如「修改資料時,id 不能改、其他欄位都是選填」:

type UserUpdate = Partial<Omit<User, 'id'>>;
// 先 Omit 掉 id,再全部變成可選

const patch: UserUpdate = { name: '小華' }; // ✅
const bad: UserUpdate = { id: 2 };
// ❌ Object literal may only specify known properties, and 'id' does not exist in type 'Partial<Omit<User, "id">>'.

圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言