149 lines
3.1 KiB
TypeScript
149 lines
3.1 KiB
TypeScript
/**
|
|
* Toolkit - Type Helpers
|
|
*
|
|
* TypeScript utility types and helper functions.
|
|
*/
|
|
|
|
/**
|
|
* Make specific properties optional
|
|
*/
|
|
export type PartialBy<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>;
|
|
|
|
/**
|
|
* Make specific properties required
|
|
*/
|
|
export type RequiredBy<T, K extends keyof T> = Omit<T, K> & Required<Pick<T, K>>;
|
|
|
|
/**
|
|
* Deep partial (makes all nested properties optional)
|
|
*/
|
|
export type DeepPartial<T> = T extends object
|
|
? { [P in keyof T]?: DeepPartial<T[P]> }
|
|
: T;
|
|
|
|
/**
|
|
* Extract keys of a certain type
|
|
*/
|
|
export type KeysOfType<T, U> = {
|
|
[K in keyof T]: T[K] extends U ? K : never;
|
|
}[keyof T];
|
|
|
|
/**
|
|
* Ensure all keys of a type are present
|
|
*/
|
|
export function ensureKeys<T extends Record<string, unknown>>(
|
|
obj: Partial<T>,
|
|
keys: (keyof T)[]
|
|
): obj is T {
|
|
return keys.every(key => key in obj);
|
|
}
|
|
|
|
/**
|
|
* Pick properties by value type
|
|
*/
|
|
export type PickByValue<T, V> = Pick<
|
|
T,
|
|
{
|
|
[K in keyof T]: T[K] extends V ? K : never;
|
|
}[keyof T]
|
|
>;
|
|
|
|
/**
|
|
* Safely access nested property
|
|
*/
|
|
export function getNestedProperty<T>(
|
|
obj: unknown,
|
|
path: string,
|
|
defaultValue?: T
|
|
): T | undefined {
|
|
const keys = path.split(".");
|
|
let current: any = obj;
|
|
|
|
for (const key of keys) {
|
|
if (current === null || current === undefined || typeof current !== "object") {
|
|
return defaultValue;
|
|
}
|
|
current = current[key];
|
|
}
|
|
|
|
return current ?? defaultValue;
|
|
}
|
|
|
|
// ============================================================================
|
|
// Async State Management
|
|
// ============================================================================
|
|
|
|
/**
|
|
* Generic async state type for handling loading/success/error states
|
|
*/
|
|
export type AsyncState<T, E = Error> =
|
|
| { status: "idle" }
|
|
| { status: "loading" }
|
|
| { status: "success"; data: T }
|
|
| { status: "error"; error: E };
|
|
|
|
/**
|
|
* Create an idle state
|
|
*/
|
|
export function createIdleState<T, E = Error>(): AsyncState<T, E> {
|
|
return { status: "idle" };
|
|
}
|
|
|
|
/**
|
|
* Create a loading state
|
|
*/
|
|
export function createLoadingState<T, E = Error>(): AsyncState<T, E> {
|
|
return { status: "loading" };
|
|
}
|
|
|
|
/**
|
|
* Create a success state with data
|
|
*/
|
|
export function createSuccessState<T, E = Error>(data: T): AsyncState<T, E> {
|
|
return { status: "success", data };
|
|
}
|
|
|
|
/**
|
|
* Create an error state
|
|
*/
|
|
export function createErrorState<T, E = Error>(error: E): AsyncState<T, E> {
|
|
return { status: "error", error };
|
|
}
|
|
|
|
/**
|
|
* Type guard: check if state is idle
|
|
*/
|
|
export function isIdle<T, E>(
|
|
state: AsyncState<T, E>
|
|
): state is { status: "idle" } {
|
|
return state.status === "idle";
|
|
}
|
|
|
|
/**
|
|
* Type guard: check if state is loading
|
|
*/
|
|
export function isLoading<T, E>(
|
|
state: AsyncState<T, E>
|
|
): state is { status: "loading" } {
|
|
return state.status === "loading";
|
|
}
|
|
|
|
/**
|
|
* Type guard: check if state is success
|
|
*/
|
|
export function isSuccess<T, E>(
|
|
state: AsyncState<T, E>
|
|
): state is { status: "success"; data: T } {
|
|
return state.status === "success";
|
|
}
|
|
|
|
/**
|
|
* Type guard: check if state is error
|
|
*/
|
|
export function isError<T, E>(
|
|
state: AsyncState<T, E>
|
|
): state is { status: "error"; error: E } {
|
|
return state.status === "error";
|
|
}
|
|
|