![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiI0gTMx81dsQWZ4lmZf1GLlpXazVmcvwFciV2dsQXYtJ3bm9CX9s2RkBnVHFmb1clWvB3MaVnRtp1XlBXe0xCMy81dvRWYoNHLwEzX5xCMx8FesU2cfdGLwMzX0xiRGZkRGZ0Xy9GbvNGLpZTY1EmMZVDUSFTU4VFRR9Fd4VGdsYTMfVmepNHLrJXYtJXZ0F2dvwVZnFWbp1zczV2YvJHctM3cv1Ce-cGcq5SO5UDN4YmMyMmMiJWYhNzMzYzX3IDMwATM4IzLclDMyIDMy8CXn9Gbi9CXzV2Zh1WavwVbvNmLvR3YxUjLyM3Lc9CX6MHc0RHaiojIsJye.jpg)
如何讓一個類的屬性全部可選?
比如我有下面這樣一個類型:
type User = {
username: string;
gender: 'male' | 'female';
age: number;
bio: string;
password: string;
}
User 類型要求所有屬性都必須有值,是以:
const user: User = {
username: 'awesomeuser',
};
是不可行的,會提示:
類型“{ username: string; }”缺少類型“User”中的以下屬性: gender, age, bio
如何讓它可行?使用 Partial 即可:
const user: Partial<User> = {
username: 'awesomeuser'
}
Partial 内置内型的作用就是讓一個已定義了的類型的所有屬性變得可選,具體實作如下:
/**
* Make all properties in T optional
*/
type Partial<T> = {
[P in keyof T]?: T[P];
};
如何讓一個類型的屬性全部必填?
假設我們有下面這樣一個類型定義:
type User = {
username: string;
age?: number;
gender?: 'male' | 'female'
bio?: string;
password?: string;
}
然後從伺服器後端拿到了一系列使用者資料的結果:
const users: User[] = await api.users.list();
此時我們嘗試去通路使用者的 age 進行比較,想要取出 age > 18 的使用者:
const filteredUsers = users.filter(user => user.age > 18);
此時你的 IDE 是不是提示你:對象可能為“未定義”。?為什麼?因為我們定義了 age 本身就是可選的屬性,未定義該屬性時,它的值就是 undefined,而在 typescript 中, undefined 是不允許與 number 類型進行比較的。
但是此時其實我們是能很确定 api.users.list 接口傳回給我的,要麼是抛出錯誤,要麼給到我的就一定是帶了 age 值的 User 類型資料,是以,我是完全可以去比較的,但是由于 TypeScript 并不知道你加載的資料是 User 還是所有屬性都已經有值的特殊的 User,那怎麼辦?什麼 Required 即可。
const users: Required<User>[] = [];
或者讓 api.users.list() 的傳回類型就是 Required<User>[] 即可。
如何自己實作 Required:
/**
* Make all properties in T required
*/
type Required<T> = {
[P in keyof T]-?: T[P];
};
如何讓一個類型的所有屬性變成隻讀?
假設我們現在在寫一個系統,當使用者登入之後,會共享一個 User 對象給所有元件,但是隻允許他人讀取其值,不允許他人修改,但是我們定義的 User 是整個系統共用的,有編輯功能的頁面也在使用這個類型定義,它們是需要能修改使用者資料的,那怎麼辦?
使用 Readonly 即可:
const user = {
username: "awesomeuser",
gender: "male",
age: 19,
bio: "Aha, insight~",
};
const sharedUser = user as Readonly<User>;
sharedUser.username = "new Name";
此時,IDE 就會告訴你無法配置設定到 "username" ,因為它是隻讀屬性。
注意:TypeScript 隻作靜态類型校驗,但是并不表示這些值真的不能被修改了,如果真的要建立一個真正從程式上都不能被修改的對象,請問怎麼解決?
我想有一個類,隻具有另一個類的部分屬性定義
還是那個 User,我想定義一個 Login 類型,它專門用于登入時的類型,但是我又不想重新去寫一遍 username 與 password 的類型定義(雖然在本例中,重新寫一遍也很簡單,但保不齊哪天就會遇到一個十分複雜的類型定義呢?),怎麼辦?使用 Pick 即可。
type User = {
username: string;
gender?: "male" | "female";
age?: number;
bio?: string;
password?: string;
};
type Login = Pick<User, "username" | "password">;
const login: Login = {
username: "pantao",
};
你們也看到了,Login 類型還是隻有 username 是必填的,password 是可選的,這與我們的要求不符,怎麼辦?加上 Required 啊。
type User = {
username: string;
gender?: "male" | "female";
age?: number;
bio?: string;
password?: string;
};
type Login = Required<Pick<User, "username" | "password">>;
const login: Login = {
username: "pantao",
};
此時就會要求你必須輸入 password 了。
如何自己實作 Pick?
/**
* From T, pick a set of properties whose keys are in the union K
*/
type Pick<T, K extends keyof T> = {
[P in K]: T[P];
};
如果快速定義一個類型中,具有相同資料類型的屬性?
假設我們現在在開發一個商城系統,每一個 Store 都有一個店長,一個倉庫管理者,一個收銀員,他們都關聯到了 User 上面,此時你可以這樣定義 Store 這個類型:
type User = {
username: string;
gender?: "male" | "female";
age?: number;
bio?: string;
password?: string;
};
type Store = {
name: string;
location: string;
leader: User;
warehouseKeeper: User;
cashier: User;
};
當然,你還可以這樣定義:
type Store = Record<'leader' | 'warehouseKeeper' | 'cashier', User> & {
name: string;
location: string;
}
兩種方式,哪種更好,當然各有優劣,但是假設我們遇到下面這樣的一個情況:
type User = {
username: string;
gender?: "male" | "female";
age?: number;
bio?: string;
password?: string;
};
const users: User = [
{
username: "awesomeuser",
},
{
username: "baduser",
},
{
username: "gooduser",
},
];
為了通路友善,我想将 users 變量轉換成一個屬性名稱為 users 數組索引,值為 users 數組元素(即 User 類型)的對象,怎麼定義這樣的一個類型?
你可以這樣:
type UserSet = { [key: number]: User };
const userSet: UserSet = users.reduce(
(set, user, index) => ({ ...set, [index]: user }),
{} as UserSet
);
你也可以這樣:
type UserSet = Record<number, User>;
如何自己實作 Record?
/**
* Construct a type with a set of properties K of type T
*/
type Record<K extends keyof any, T> = {
[P in K]: T;
};
本文完~
學習更多技能
請點選下方公衆号