utils.ts 1.6 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556
  1. export type Nullable<T> = T | null
  2. // eslint-disable-next-line @typescript-eslint/no-empty-interface, @typescript-eslint/no-empty-object-type
  3. interface DeepReadonlyArray<T> extends ReadonlyArray<DeepReadonly<T>> {}
  4. type DeepReadonlyObject<T> = {
  5. readonly [P in keyof T]: DeepReadonly<T[P]>
  6. }
  7. export type DeepReadonly<T> = T extends (infer R)[]
  8. ? DeepReadonlyArray<R>
  9. : T extends (...args: any[]) => void
  10. ? T
  11. : T extends object
  12. ? DeepReadonlyObject<T>
  13. : T
  14. export type DeepPartial<T> = Partial<{ [P in keyof T]: DeepPartial<T[P]> }>
  15. export type MergeAndOverride<Parent, Own> = Own & Omit<Parent, keyof Own>
  16. export type Keys<T extends object> = (keyof T)[]
  17. export type ExcludeStrict<T, U extends T> = Exclude<T, U>
  18. /**
  19. * Helper to create type guards for literal unions
  20. *
  21. * @example
  22. * ```ts
  23. * const fruit = ['apple', 'banana', 'cherry'] as const;
  24. * type Fruit = typeof fruit[number];
  25. * const isFruit = mkLiteralUnionTypeguard(fruit);
  26. *
  27. * // Usage example:
  28. * function eatFood(food: unknown[]) {
  29. * food.forEach(item => {
  30. * if (isFruit(item)) {
  31. * eatFruit(item)
  32. * } else {
  33. * console.log(`Not fruit ${item}`)
  34. * }
  35. * })
  36. * }
  37. * eatFood(['banana', 'pizza'])
  38. * ```
  39. *
  40. * @param xs A readonly tuple of allowed values (strings, numbers, or symbols).
  41. * @returns A type guard function `(value: unknown) => value is T[number]`.
  42. */
  43. export function mkLiteralUnionTypeguard<
  44. const T extends readonly (string | number | symbol)[],
  45. >(xs: T) {
  46. return (v: unknown): v is T[number] =>
  47. (xs as readonly (string | number | symbol)[]).includes(v as any)
  48. }