ts-type
Version:
TypeScript 類型工具庫:提供豐富的類型操作工具和重新導出的內建類型 / TypeScript type utility library: provides rich type manipulation utilities and re-exported built-in types
231 lines (230 loc) • 9 kB
TypeScript
/**
* 記錄(Record)類型操作工具
* Record Type Manipulation Utilities
*
* 提供物件類型選擇、覆寫、合併等操作
* Provides object type selection, overwrite, merge and other operations
*
* Created by user on 2019/6/11.
*/
import type { ITSKeyofBothDiff, ITSKeyofBothSame, ITSKeyofDiff, ITSKeyofSame } from '../helper/filter';
export type { ITSRequireRecord, ITSPartialRecord } from '../type/record/partial';
/**
* 取得兩個鍵集合的差異(已棄用)
* Get the difference between two key sets (deprecated)
*
* @deprecated 請使用 ITSKeyofDiff 取代 / Please use ITSKeyofDiff instead
*/
export type ITSDiff<T extends keyof any, U extends keyof any> = ({
[P in T]: P;
} & {
[P in U]: never;
} & {
[x: string]: never;
})[T];
/**
* 選擇 T 中與 U 相同的鍵
* Pick keys from T that are the same as U
*
* @example
* type A = { a: 1; b: 2; };
* type B = { a: 3; c: 4; };
* type Result = ITSPickSame<A, B>;
* // type Result = { a: 1 }
*/
export type ITSPickSame<T, U> = Pick<T, ITSKeyofSame<T, U>>;
/**
* 選擇 T 中與 U 不同的鍵
* Pick keys from T that are different from U
*
* @example
* type A = { a: 1; b: 2; };
* type B = { a: 3; c: 4; };
* type Result = ITSPickDiff<A, B>;
* // type Result = { b: 2 }
*/
export type ITSPickDiff<T, U> = Pick<T, ITSKeyofDiff<T, U>>;
/**
* 選擇 T 與 U 中同時存在的鍵
* Pick keys that exist in both T and U
*
* @example
* type A = { a: 1; b: 2; };
* type B = { a: 3; c: 4; };
* type Result = ITSPickBothSame<A, B>;
* // type Result = { a: 1 }
*/
export type ITSPickBothSame<T, U> = Pick<T & U, ITSKeyofBothSame<T, U>>;
/**
* 選擇 T 與 U 中不同時存在的鍵
* Pick keys that do not exist in both T and U
*
* @example
* type A = { a: 1; b: 2; };
* type B = { a: 3; c: 4; };
* type Result = ITSPickBothDiff<A, B>;
* // type Result = { b: 2; c: 4 }
*/
export type ITSPickBothDiff<T, U> = Pick<T & U, ITSKeyofBothDiff<T, U>>;
/**
* 選擇指定鍵集合 K(必須是 T 與 U 中同時存在的鍵)
* Pick a specified key set K (must be keys that exist in both T and U)
*/
export type ITSPickBoth<T, U, K extends ITSKeyofBothSame<T, U> = ITSKeyofBothSame<T, U>> = Pick<T & U, K>;
/**
* 取得物件成員的類型
* Get the type of an object member
*
* @see https://stackoverflow.com/questions/49198713/override-the-properties-of-an-interface-in-typescript
*/
export type ITSPickMember<T, K extends keyof T> = T[K];
/**
* 排除指定鍵(已棄用)
* Exclude specified keys (deprecated)
*
* @deprecated 請使用 Omit 取代 / Please use Omit instead
*/
export type ITSPickNot<T, K extends keyof T> = Omit<T, K>;
/**
* 覆寫物件類型的屬性
* Overwrite properties of an object type
*
* 用 U 中的屬性覆寫 T 中的同名屬性
* Overwrites properties in T with the same name from U
*
* @see https://stackoverflow.com/questions/49198713/override-the-properties-of-an-interface-in-typescript
*
* @example
* interface A1 { s: string }
* type A2 = ITSOverwrite<A1, { s: number }>;
* // type A2 = { s: number }
*/
export type ITSOverwrite<T, U> = Omit<T, keyof U> & U;
/**
* 合併兩個物件類型,處理衝突屬性
* Merge two object types, handling conflicting properties
*
* 對於 T 與 U 中同時存在的鍵,取兩者的聯集類型
* For keys that exist in both T and U, takes the union type of both
*
* @example
* type Test1 = { id: number, code: string }
* type Test2 = { id: string, code: number }
* type Test3 = ITSMergeBoth<Test1, Test2>
* // type Test3 = { id: string | number; code: string | number }
* @see https://github.com/microsoft/TypeScript/issues/35627
*/
export type ITSMergeBoth<T, U> = ITSPickBothDiff<T, U> & Pick<T | U, ITSKeyofBothSame<T, U>>;
/**
* 選擇指定鍵並設為必填
* Pick specified keys and mark as Required
*
* @example
* interface User { name?: string; age?: number; email?: string; }
* type RequiredName = ITSRequiredPick<User, 'name'>;
* // type RequiredName = { name: string; age?: number; email?: string; }
*/
export type ITSRequiredPick<T, K extends keyof T = keyof T> = {
[P in K]-?: T[P];
};
/**
* 選擇指定鍵並設為可選
* Pick specified keys and mark as Partial
*
* @example
* interface User { name: string; age: number; email: string; }
* type PartialName = ITSPartialPick<User, 'name'>;
* // type PartialName = { name?: string; age: number; email: string; }
*/
export type ITSPartialPick<T, K extends keyof T = keyof T> = {
[P in K]?: T[P];
};
/**
* 複製類型並將指定鍵RK設為必填,其他鍵Pk設為可選
* Clone a type and mark specified keys as Required, other keys as Partial
*
* @example
* interface User { name: string; age: number; email: string; }
* type Result = ITSPickExtra<User, 'name', 'email'>;
* // type Result = { name: string; } & { age?: number; email?: string; }
*/
export type ITSPickExtra<T, RK extends keyof T, PK extends Exclude<keyof T, RK> = Exclude<keyof T, RK>> = ITSRequiredPick<T, RK> & ITSPartialPick<T, PK>;
/**
* 複製類型並將指定鍵Pk設為可選,其他鍵Rk設為必填(與 ITSPickExtra 相反)
* Clone a type and mark specified keys as Partial, other keys as Required (opposite of ITSPickExtra)
*
* @example
* interface User { name: string; age: number; email: string; }
* type Result = ITSPickExtra2<User, 'name', 'email'>;
* // type Result = { name?: string; } & { age: number; email: string; }
*/
export type ITSPickExtra2<T, PK extends keyof T, RK extends Exclude<keyof T, PK> = Exclude<keyof T, PK>> = ITSRequiredPick<T, RK> & ITSPartialPick<T, PK>;
/**
* 保留指定鍵不變(維持原本的必選/可選/唯讀屬性),其他鍵變為可選
* Keep specified keys unchanged (preserving required/optional/readonly modifiers), other keys become Partial
*
* 與 ITSPickExtra 的差異:ITSPickExtra 會將指定鍵強制設為必填,而此類型保留原始修飾符
* Difference from ITSPickExtra: ITSPickExtra forces specified keys to Required, while this type preserves original modifiers
*
* @see {@link ITSPickExtra} - 將指定鍵設為必填,其他鍵可選
* @see {@link ITSPartialWith} - 將指定鍵設為可選,其他鍵不變
*
* @example
* interface User { name: string; readonly id: string; age?: number; email: string; }
* type Result = ITSPickAndPartialOther<User, 'name' | 'id'>;
* // 輸出結果 / Output:
* // { name: string; readonly id: string; } & { age?: number; email?: string; }
* // 注意:name 維持必選,id 維持唯讀且必選,age 和 email 變為可選
*/
export type ITSPickAndPartialOther<T, RK extends keyof T, PK extends Exclude<keyof T, RK> = Exclude<keyof T, RK>> = Pick<T, RK> & ITSPartialPick<T, PK>;
/**
* 保留指定鍵為必填,其他鍵不變
* Keep specified keys as Required, other keys unchanged
*
* @example
* interface User { name: string; age: number; email: string; }
* type Result = ITSRequiredWith<User, 'name'>;
* // type Result = { name: string; age: number; email: string; }
*/
export type ITSRequiredWith<T, K extends keyof T> = Omit<T, K> & ITSRequiredPick<T, K>;
/**
* 保留指定鍵為可選,其他鍵不變
* Keep specified keys as Partial, other keys unchanged
*
* @example
* interface User { name: string; age: number; email: string; }
* type Result = ITSPartialWith<User, 'name'>;
* // type Result = { name?: string; age: number; email: string; }
*/
export type ITSPartialWith<T, K extends keyof T> = Omit<T, K> & ITSPartialPick<T, K>;
/**
* 確保物件至少具有指定的鍵集合中的一個
* Ensure the object has at least one of the specified key sets
*
* @see https://stackoverflow.com/questions/40510611/typescript-interface-require-one-of-two-properties-to-exist
*
* @example
* interface User { name?: string; age?: number; }
* type AtLeastOne = ITSRequireAtLeastOne<User, 'name' | 'age'>;
* // 需要至少提供 name 或 age 其中一個
*/
export type ITSRequireAtLeastOne<T, Keys extends keyof T = keyof T> = Omit<T, Keys> & {
[K in Keys]-?: ITSRequiredPick<T, K> & ITSPartialPick<T, Exclude<Keys, K>>;
}[Keys];
/**
* 確保物件只能具有指定的鍵集合中的其中一個(互斥)
* Ensure the object can only have exactly one of the specified key sets (mutually exclusive)
*
* @see https://stackoverflow.com/questions/40510611/typescript-interface-require-one-of-two-properties-to-exist
* @see {@link ITSPickOne} 另一種實現方式,使用 `void` 作為其餘鍵的類型
*
* @example
* interface User { name?: string; age?: number; }
* type OnlyOne1 = ITSRequireOnlyOne<User, 'name' | 'age'>;
* // 輸出結果:
* // type OnlyOne1 = { name: string; age?: never; } | { age: number; name?: never; }
* // 只能提供 name 或 age 其中一個,不能同時提供
*/
export type ITSRequireOnlyOne<T, Keys extends keyof T = keyof T> = Omit<T, Keys> & {
[K in Keys]-?: ITSRequiredPick<T, K> & Partial<Record<Exclude<Keys, K>, never>>;
}[Keys];