Skip to content

TypeScript 型別宣告技巧

當專案變大時,資料間的關係會變複雜,宣告型別的方式就會變得很重要,以下是一些型別宣告的技巧:

1. PartialPickOmit

Partial 將型別的屬性變為可選的
Pick 從型別中選擇某些屬性
Omit 從型別中排除某些屬性

typescript
interface User {
    id: number;
    name: string;
    email: string;
}
// 將 User 型別的所有屬性變為可選的,Partial 是 TypeScript 2.1 新增的工具型別,用來將一個型別的所有屬性變為可選的。
type PartialUser = Partial<User>;
// 從 User 型別中選擇 name 和 email 屬性,Pick 是 TypeScript 2.1 新增的工具型別,用來從一個型別中選擇某些屬性。
type UserNameAndEmail = Pick<User, 'name' | 'email'>;
// 從 User 型別中排除 email 屬性,Omit 是 TypeScript 4.1 新增的工具型別,用來從一個型別中排除某些屬性。
type UserWithoutEmail = Omit<User, 'email'>;

2. Extract

從聯合型別中提取特定型別

typescript
type UserInput = string | number | Date | string[];
type TextualInput = Extract<UserInput, string | string[]>;

function handleText(input: TextualInput) {
    console.log(`Handling text: ${input}`);
}

// 正確的用法
// 不會有 TypeScript 類型錯誤
handleText("Hello, world!");
handleText(["Hello", "world!"]);

// 錯誤的用法
handleText(42); // TypeScript 類型錯誤
handleText(new Date()); // TypeScript 類型錯誤

3. Exclude

從聯合型別中排除某些型別

typescript
type UserInput = string | number | Date | string[];
type NonTextualInput = Exclude<UserInput, string | string[]>;

function handleNonText(input: NonTextualInput) {
    console.log(`Handling non-textual input: ${input}`);
}

4. readonly

使屬性不可變

typescript
interface User {
    readonly id: number;
    name: string;
    email: string;
}

5. Record

將一組 key 與 value 型別組合成一個物件型別。

typescript
type User = {
    id: number;
    name: string;
    email: string;
};
type UserRecord = Record<string, User>;
const users: UserRecord = {
    "1": { id: 1, name: "John", email: "[email protected]" },
    "2": { id: 2, name: "Jane", email: "[email protected]" },
};

Record<string, User> 表示這是一個物件,其所有的鍵(key)都是 string,而對應的值(value)都是 User 型別。

6. Creating types from values in array

限制一個變數的值必須在陣列中
https://github.com/microsoft/TypeScript/issues/28046

typescript
const animals = ['cat', 'dog', 'mouse'] as const
type Animal = typeof animals[number]

// type Animal = 'cat' | 'dog' | 'mouse'

7. ParametersReturnType

提取函式的參數型別與返回型別

常見於套件沒有 export 型別時,可以用 typeof 搭配這兩個工具型別來提取型別資訊。

Parameters

用來從函式型別中提取參數型別為一個 tuple:

ts
function greet(name: string, age: number) {
  return `Hello, ${name}, age ${age}`;
}

type GreetParams = Parameters<typeof greet>;
// GreetParams = [string, number]

ReturnType

用來提取函式的返回值型別:

ts
type GreetReturn = ReturnType<typeof greet>;
// GreetReturn = string

搭配 async 函式

如果函式是 async,可以使用 Awaited 解開 Promise:

ts
async function fetchData(): Promise<number> {
  return 42;
}

type Fetched = Awaited<ReturnType<typeof fetchData>>;
// Fetched = number