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