天天看點

TypeScript進階的用法Partial、Required、Readonly

TypeScript進階的用法Partial、Required、Readonly

如何讓一個類的屬性全部可選?

比如我有下面這樣一個類型:

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;
};      

本文完~

學習更多技能

請點選下方公衆号