type AnyValue = | { [key: string]: AnyValue } | { [key: number]: AnyValue } | AnyValue[] | string | number | boolean | null | undefined | symbol | BigInt | ((...args: AnyValue[]) => AnyValue); type SerializableValue = | { [key: string]: SerializableValue } | { [key: number]: SerializableValue } | SerializableValue[] | string | number | boolean | null | undefined; export default function toSerializable< TInput extends { [key: string]: AnyValue } | { [key: number]: AnyValue } | AnyValue[] | AnyValue, >(input: TInput): SerializableValue { if (input == null) return input; return Array.isArray(input) ? input .filter(shouldKeep) .map((item) => (typeof item === 'object' ? toSerializable(item) : item)) : Object.fromEntries( Object.entries(input) .filter(([, v]) => shouldKeep(v)) .map(([key, value]) => [key, typeof value === 'object' ? toSerializable(value) : value]), ); } // eslint-disable-next-line @typescript-eslint/ban-types type MakeSerializable = TInput extends BigInt | symbol | Function ? never : TInput; function shouldKeep(item: TItem): item is MakeSerializable { return !['function', 'symbol', 'bigint'].includes(typeof item); }