UNPKG

reselect

Version:
877 lines (807 loc) 29.6 kB
import type { MergeParameters } from './versionedTypes' import type { weakMapMemoize } from './weakMapMemoize' export type { MergeParameters } from './versionedTypes' /* * ----------------------------------------------------------------------------- * ----------------------------------------------------------------------------- * * Reselect Data Types * * ----------------------------------------------------------------------------- * ----------------------------------------------------------------------------- */ /** * A standard selector function. * @template State - The first value, often a Redux root state object. * @template Result - The final result returned by the selector. * @template Params - All additional arguments passed into the selector. * * @public */ export type Selector< State = any, Result = unknown, Params extends readonly any[] = any[] > = Distribute< /** * A function that takes a state and returns data that is based on that state. * * @param state - The first argument, often a Redux root state object. * @param params - All additional arguments passed into the selector. * @returns A derived value from the state. */ (state: State, ...params: FallbackIfNever<Params, []>) => Result > /** * An array of input selectors. * * @public */ export type SelectorArray<State = any> = readonly Selector<State>[] /** * Extracts an array of all return types from all input selectors. * * @public */ export type SelectorResultArray<Selectors extends SelectorArray> = ExtractReturnType<Selectors> /** * The options object used inside `createSelector` and `createSelectorCreator`. * * @template MemoizeFunction - The type of the memoize function that is used to memoize the `resultFunc` inside `createSelector` (e.g., `lruMemoize` or `weakMapMemoize`). * @template ArgsMemoizeFunction - The type of the optional memoize function that is used to memoize the arguments passed into the output selector generated by `createSelector` (e.g., `lruMemoize` or `weakMapMemoize`). If none is explicitly provided, `weakMapMemoize` will be used. * @template OverrideMemoizeFunction - The type of the optional `memoize` function that could be passed into the options object inside `createSelector` to override the original `memoize` function that was initially passed into `createSelectorCreator`. * @template OverrideArgsMemoizeFunction - The type of the optional `argsMemoize` function that could be passed into the options object inside `createSelector` to override the original `argsMemoize` function that was initially passed into `createSelectorCreator`. If none was initially provided, `weakMapMemoize` will be used. * * @public */ export interface CreateSelectorOptions< MemoizeFunction extends UnknownMemoizer = typeof weakMapMemoize, ArgsMemoizeFunction extends UnknownMemoizer = typeof weakMapMemoize, OverrideMemoizeFunction extends UnknownMemoizer = never, OverrideArgsMemoizeFunction extends UnknownMemoizer = never > { /** * Reselect performs additional checks in development mode to help identify * and warn about potential issues in selector behavior. This option * allows you to customize the behavior of these checks per selector. * * @see {@link https://reselect.js.org/api/development-only-stability-checks Development-Only Stability Checks} * * @since 5.0.0 */ devModeChecks?: Partial<DevModeChecks> /** * The memoize function that is used to memoize the {@linkcode OutputSelectorFields.resultFunc resultFunc} * inside `createSelector` (e.g., `lruMemoize` or `weakMapMemoize`). * * When passed directly into `createSelector`, it overrides the `memoize` function initially passed into `createSelectorCreator`. * * @example * ```ts * import { createSelector, weakMapMemoize } from 'reselect' * * const selectItemsByCategory = createSelector( * [ * (state: RootState) => state.items, * (state: RootState, category: string) => category * ], * (items, category) => items.filter(item => item.category === category), * { memoize: weakMapMemoize } * ) * ``` * * @since 5.0.0 */ memoize?: FallbackIfNever<OverrideMemoizeFunction, MemoizeFunction> /** * The optional memoize function that is used to memoize the arguments * passed into the output selector generated by `createSelector` * (e.g., `lruMemoize` or `weakMapMemoize`). * * When passed directly into `createSelector`, it overrides the * `argsMemoize` function initially passed into `createSelectorCreator`. * If none was initially provided, `weakMapMemoize` will be used. * * @example * ```ts * import { createSelector, weakMapMemoize } from 'reselect' * * const selectItemsByCategory = createSelector( * [ * (state: RootState) => state.items, * (state: RootState, category: string) => category * ], * (items, category) => items.filter(item => item.category === category), * { argsMemoize: weakMapMemoize } * ) * ``` * * @default weakMapMemoize * * @since 5.0.0 */ argsMemoize?: FallbackIfNever< OverrideArgsMemoizeFunction, ArgsMemoizeFunction > /** * Optional configuration options for the {@linkcode CreateSelectorOptions.memoize memoize} function. * These options are passed to the {@linkcode CreateSelectorOptions.memoize memoize} function as the second argument. * * @since 5.0.0 */ memoizeOptions?: OverrideMemoizeOptions< MemoizeFunction, OverrideMemoizeFunction > /** * Optional configuration options for the {@linkcode CreateSelectorOptions.argsMemoize argsMemoize} function. * These options are passed to the {@linkcode CreateSelectorOptions.argsMemoize argsMemoize} function as the second argument. * * @since 5.0.0 */ argsMemoizeOptions?: OverrideMemoizeOptions< ArgsMemoizeFunction, OverrideArgsMemoizeFunction > } /** * The additional fields attached to the output selector generated by `createSelector`. * * **Note**: Although {@linkcode CreateSelectorOptions.memoize memoize} * and {@linkcode CreateSelectorOptions.argsMemoize argsMemoize} are included in the attached fields, * the fields themselves are independent of the type of * {@linkcode CreateSelectorOptions.memoize memoize} and {@linkcode CreateSelectorOptions.argsMemoize argsMemoize} functions. * Meaning this type is not going to generate additional fields based on what functions we use to memoize our selectors. * * _This type is not to be confused with {@linkcode ExtractMemoizerFields ExtractMemoizerFields}._ * * @template InputSelectors - The type of the input selectors. * @template Result - The type of the result returned by the `resultFunc`. * @template MemoizeFunction - The type of the memoize function that is used to memoize the `resultFunc` inside `createSelector` (e.g., `lruMemoize` or `weakMapMemoize`). * @template ArgsMemoizeFunction - The type of the optional memoize function that is used to memoize the arguments passed into the output selector generated by `createSelector` (e.g., `lruMemoize` or `weakMapMemoize`). If none is explicitly provided, `weakMapMemoize` will be used. * * @public */ export type OutputSelectorFields< InputSelectors extends SelectorArray = SelectorArray, Result = unknown, MemoizeFunction extends UnknownMemoizer = typeof weakMapMemoize, ArgsMemoizeFunction extends UnknownMemoizer = typeof weakMapMemoize > = { /** * The final function passed to `createSelector`. Otherwise known as the `combiner`. */ resultFunc: Combiner<InputSelectors, Result> /** * The memoized version of {@linkcode OutputSelectorFields.resultFunc resultFunc}. */ memoizedResultFunc: Combiner<InputSelectors, Result> & ExtractMemoizerFields<MemoizeFunction> /** * @Returns The last result calculated by {@linkcode OutputSelectorFields.memoizedResultFunc memoizedResultFunc}. */ lastResult: () => Result /** * The array of the input selectors used by `createSelector` to compose the * combiner ({@linkcode OutputSelectorFields.memoizedResultFunc memoizedResultFunc}). */ dependencies: InputSelectors /** * Counts the number of times {@linkcode OutputSelectorFields.memoizedResultFunc memoizedResultFunc} has been recalculated. */ recomputations: () => number /** * Resets the count of {@linkcode OutputSelectorFields.recomputations recomputations} count to 0. */ resetRecomputations: () => void /** * Counts the number of times the input selectors ({@linkcode OutputSelectorFields.dependencies dependencies}) * have been recalculated. This is distinct from {@linkcode OutputSelectorFields.recomputations recomputations}, * which tracks the recalculations of the result function. * * @since 5.0.0 */ dependencyRecomputations: () => number /** * Resets the count {@linkcode OutputSelectorFields.dependencyRecomputations dependencyRecomputations} * for the input selectors ({@linkcode OutputSelectorFields.dependencies dependencies}) * of a memoized selector. * * @since 5.0.0 */ resetDependencyRecomputations: () => void } & Simplify< Required< Pick< CreateSelectorOptions<MemoizeFunction, ArgsMemoizeFunction>, 'argsMemoize' | 'memoize' > > > /** * Represents the actual selectors generated by `createSelector`. * * @template InputSelectors - The type of the input selectors. * @template Result - The type of the result returned by the `resultFunc`. * @template MemoizeFunction - The type of the memoize function that is used to memoize the `resultFunc` inside `createSelector` (e.g., `lruMemoize` or `weakMapMemoize`). * @template ArgsMemoizeFunction - The type of the optional memoize function that is used to memoize the arguments passed into the output selector generated by `createSelector` (e.g., `lruMemoize` or `weakMapMemoize`). If none is explicitly provided, `weakMapMemoize` will be used. * * @public */ export type OutputSelector< InputSelectors extends SelectorArray = SelectorArray, Result = unknown, MemoizeFunction extends UnknownMemoizer = typeof weakMapMemoize, ArgsMemoizeFunction extends UnknownMemoizer = typeof weakMapMemoize > = Selector< GetStateFromSelectors<InputSelectors>, Result, GetParamsFromSelectors<InputSelectors> > & ExtractMemoizerFields<ArgsMemoizeFunction> & OutputSelectorFields< InputSelectors, Result, MemoizeFunction, ArgsMemoizeFunction > /** * A function that takes input selectors' return values as arguments and returns a result. Otherwise known as `resultFunc`. * * @template InputSelectors - An array of input selectors. * @template Result - Result returned by `resultFunc`. * * @public */ export type Combiner<InputSelectors extends SelectorArray, Result> = Distribute< /** * A function that takes input selectors' return values as arguments and returns a result. Otherwise known as `resultFunc`. * * @param resultFuncArgs - Return values of input selectors. * @returns The return value of {@linkcode OutputSelectorFields.resultFunc resultFunc}. */ (...resultFuncArgs: SelectorResultArray<InputSelectors>) => Result > /** * A standard function returning true if two values are considered equal. * * @public */ export type EqualityFn<T = any> = (a: T, b: T) => boolean /** * The frequency of development mode checks. * * @since 5.0.0 * @public */ export type DevModeCheckFrequency = 'always' | 'once' | 'never' /** * Represents the configuration for development mode checks. * * @since 5.0.0 * @public */ export interface DevModeChecks { /** * Overrides the global input stability check for the selector. * - `once` - Run only the first time the selector is called. * - `always` - Run every time the selector is called. * - `never` - Never run the input stability check. * * @default 'once' * * @see {@link https://reselect.js.org/api/development-only-stability-checks Development-Only Stability Checks} * @see {@link https://reselect.js.org/api/development-only-stability-checks#inputstabilitycheck `inputStabilityCheck`} * @see {@link https://reselect.js.org/api/development-only-stability-checks#2-per-selector-by-passing-an-inputstabilitycheck-option-directly-to- per-selector-configuration} * * @since 5.0.0 */ inputStabilityCheck: DevModeCheckFrequency /** * Overrides the global identity function check for the selector. * - `once` - Run only the first time the selector is called. * - `always` - Run every time the selector is called. * - `never` - Never run the identity function check. * * @default 'once' * * @see {@link https://reselect.js.org/api/development-only-stability-checks Development-Only Stability Checks} * @see {@link https://reselect.js.org/api/development-only-stability-checks#identityfunctioncheck `identityFunctionCheck`} * @see {@link https://reselect.js.org/api/development-only-stability-checks#2-per-selector-by-passing-an-identityfunctioncheck-option-directly-to- per-selector-configuration} * * @since 5.0.0 */ identityFunctionCheck: DevModeCheckFrequency } /** * Represents execution information for development mode checks. * * @public * @since 5.0.0 */ export type DevModeChecksExecutionInfo = { [K in keyof DevModeChecks]: { /** * A boolean indicating whether the check should be executed. */ shouldRun: boolean /** * The function to execute for the check. */ run: AnyFunction } } /** * Determines the combined single "State" type (first arg) from all input selectors. * * @public */ export type GetStateFromSelectors<Selectors extends SelectorArray> = MergeParameters<Selectors>[0] /** * Determines the combined "Params" type (all remaining args) from all input selectors. * * @public */ export type GetParamsFromSelectors<Selectors extends SelectorArray> = ArrayTail< MergeParameters<Selectors> > /** * Any Memoizer function. A memoizer is a function that accepts another function and returns it. * * @template FunctionType - The type of the function that is memoized. * * @public */ export type UnknownMemoizer< FunctionType extends UnknownFunction = UnknownFunction > = (func: FunctionType, ...options: any[]) => FunctionType /** * Extracts the options type for a memoization function based on its parameters. * The first parameter of the function is expected to be the function to be memoized, * followed by options for the memoization process. * * @template MemoizeFunction - The type of the memoize function to be checked. * * @public */ export type MemoizeOptionsFromParameters< MemoizeFunction extends UnknownMemoizer > = | ( | NonFunctionType<DropFirstParameter<MemoizeFunction>[0]> | FunctionType<DropFirstParameter<MemoizeFunction>[0]> ) | ( | NonFunctionType<DropFirstParameter<MemoizeFunction>[number]> | FunctionType<DropFirstParameter<MemoizeFunction>[number]> )[] /** * Derive the type of memoize options object based on whether the memoize function itself was overridden. * * _This type can be used for both `memoizeOptions` and `argsMemoizeOptions`._ * * @template MemoizeFunction - The type of the `memoize` or `argsMemoize` function initially passed into `createSelectorCreator`. * @template OverrideMemoizeFunction - The type of the optional `memoize` or `argsMemoize` function passed directly into `createSelector` which then overrides the original `memoize` or `argsMemoize` function passed into `createSelectorCreator`. * * @public */ export type OverrideMemoizeOptions< MemoizeFunction extends UnknownMemoizer, OverrideMemoizeFunction extends UnknownMemoizer = never > = IfNever< OverrideMemoizeFunction, Simplify<MemoizeOptionsFromParameters<MemoizeFunction>>, Simplify<MemoizeOptionsFromParameters<OverrideMemoizeFunction>> > /** * Extracts the additional properties or methods that a memoize function attaches to * the function it memoizes (e.g., `clearCache`). * * @template MemoizeFunction - The type of the memoize function to be checked. * * @public */ export type ExtractMemoizerFields<MemoizeFunction extends UnknownMemoizer> = Simplify<OmitIndexSignature<ReturnType<MemoizeFunction>>> /** * Represents the additional properties attached to a function memoized by `reselect`. * * `lruMemoize`, `weakMapMemoize` and `autotrackMemoize` all return these properties. * * @see {@linkcode ExtractMemoizerFields ExtractMemoizerFields} * * @public */ export type DefaultMemoizeFields = { /** * Clears the memoization cache associated with a memoized function. * This method is typically used to reset the state of the cache, allowing * for the garbage collection of previously memoized results and ensuring * that future calls to the function recompute the results. */ clearCache: () => void resultsCount: () => number resetResultsCount: () => void } /* * ----------------------------------------------------------------------------- * ----------------------------------------------------------------------------- * * Reselect Internal Utility Types * * ----------------------------------------------------------------------------- * ----------------------------------------------------------------------------- */ /** * Any function with any arguments. * * @internal */ export type AnyFunction = (...args: any[]) => any /** * Any function with unknown arguments. * * @internal */ export type UnknownFunction = (...args: unknown[]) => unknown /** * When a generic type parameter is using its default value of `never`, fallback to a different type. * * @template T - Type to be checked. * @template FallbackTo - Type to fallback to if `T` resolves to `never`. * * @internal */ export type FallbackIfNever<T, FallbackTo> = IfNever<T, FallbackTo, T> /** * Extracts the non-function part of a type. * * @template T - The input type to be refined by excluding function types and index signatures. * * @internal */ export type NonFunctionType<T> = Simplify< OmitIndexSignature<Exclude<T, AnyFunction>> > /** * Extracts the function part of a type. * * @template T - The input type to be refined by extracting function types. * * @internal */ export type FunctionType<T> = Extract<T, AnyFunction> /** * Extracts the return type from all functions as a tuple. * * @internal */ export type ExtractReturnType<FunctionsArray extends readonly AnyFunction[]> = { [Index in keyof FunctionsArray]: FunctionsArray[Index] extends FunctionsArray[number] ? FallbackIfUnknown<ReturnType<FunctionsArray[Index]>, any> : never } /** * Utility type to infer the type of "all params of a function except the first", * so we can determine what arguments a memoize function accepts. * * @internal */ export type DropFirstParameter<Func extends AnyFunction> = Func extends ( firstArg: any, ...restArgs: infer Rest ) => any ? Rest : never /** * Distributes over a type. It is used mostly to expand a function type * in hover previews while preserving their original JSDoc information. * * If preserving JSDoc information is not a concern, you can use {@linkcode ExpandFunction ExpandFunction}. * * @template T The type to be distributed. * * @internal */ export type Distribute<T> = T extends T ? T : never /** * Extracts the type of the first element of an array or tuple. * * @internal */ export type FirstArrayElement<ArrayType> = ArrayType extends readonly [ unknown, ...unknown[] ] ? ArrayType[0] : never /** * Extracts the type of an array or tuple minus the first element. * * @internal */ export type ArrayTail<ArrayType> = ArrayType extends readonly [ unknown, ...infer Tail ] ? Tail : [] /** * An alias for type `{}`. Represents any value that is not `null` or `undefined`. * It is mostly used for semantic purposes to help distinguish between an * empty object type and `{}` as they are not the same. * * @internal */ export type AnyNonNullishValue = NonNullable<unknown> /** * Same as {@linkcode AnyNonNullishValue AnyNonNullishValue} but aliased * for semantic purposes. It is intended to be used in scenarios where * a recursive type definition needs to be interrupted to ensure type safety * and to avoid excessively deep recursion that could lead to performance issues. * * @internal */ export type InterruptRecursion = AnyNonNullishValue /* * ----------------------------------------------------------------------------- * ----------------------------------------------------------------------------- * * External/Copied Utility Types * * ----------------------------------------------------------------------------- * ----------------------------------------------------------------------------- * */ /** * An if-else-like type that resolves depending on whether the given type is `never`. * This is mainly used to conditionally resolve the type of a `memoizeOptions` object based on whether `memoize` is provided or not. * @see {@link https://github.com/sindresorhus/type-fest/blob/main/source/if-never.d.ts Source} * * @internal */ export type IfNever<T, TypeIfNever, TypeIfNotNever> = [T] extends [never] ? TypeIfNever : TypeIfNotNever /** * Omit any index signatures from the given object type, leaving only explicitly defined properties. * This is mainly used to remove explicit `any`s from the return type of some memoizers (e.g, `microMemoize`). * * __Disclaimer:__ When used on an intersection of a function and an object, * the function is erased. * * @see {@link https://github.com/sindresorhus/type-fest/blob/main/source/omit-index-signature.d.ts Source} * * @internal */ export type OmitIndexSignature<ObjectType> = { [KeyType in keyof ObjectType as {} extends Record<KeyType, unknown> ? never : KeyType]: ObjectType[KeyType] } /** * The infamous "convert a union type to an intersection type" hack * @see {@link https://github.com/sindresorhus/type-fest/blob/main/source/union-to-intersection.d.ts Source} * @see {@link https://github.com/microsoft/TypeScript/issues/29594 Reference} * * @internal */ export type UnionToIntersection<Union> = // `extends unknown` is always going to be the case and is used to convert the // `Union` into a [distributive conditional // type](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-8.html#distributive-conditional-types). ( Union extends unknown ? // The union type is used as the only argument to a function since the union // of function arguments is an intersection. (distributedUnion: Union) => void : // This won't happen. never ) extends // Infer the `Intersection` type since TypeScript represents the positional // arguments of unions of functions as an intersection of the union. (mergedIntersection: infer Intersection) => void ? // The `& Union` is to allow indexing by the resulting type Intersection & Union : never /** * Code to convert a union of values into a tuple. * @see {@link https://stackoverflow.com/a/55128956/62937 Source} * * @internal */ type Push<T extends any[], V> = [...T, V] /** * @see {@link https://stackoverflow.com/a/55128956/62937 Source} * * @internal */ type LastOf<T> = UnionToIntersection< T extends any ? () => T : never > extends () => infer R ? R : never /** * TS4.1+ * @see {@link https://stackoverflow.com/a/55128956/62937 Source} * * @internal */ export type TuplifyUnion< T, L = LastOf<T>, N = [T] extends [never] ? true : false > = true extends N ? [] : Push<TuplifyUnion<Exclude<T, L>>, L> /** * Converts "the values of an object" into a tuple, like a type-level `Object.values()` * @see {@link https://stackoverflow.com/a/68695508/62937 Source} * * @internal */ export type ObjectValuesToTuple< T, KS extends any[] = TuplifyUnion<keyof T>, R extends any[] = [] > = KS extends [infer K, ...infer KT] ? ObjectValuesToTuple<T, KT, [...R, T[K & keyof T]]> : R /** * Create a type that makes the given keys required. * The remaining keys are kept as is. * * @see {@link https://github.com/sindresorhus/type-fest/blob/main/source/set-required.d.ts Source} * * @internal */ export type SetRequired<BaseType, Keys extends keyof BaseType> = Omit< BaseType, Keys > & Required<Pick<BaseType, Keys>> /** * An if-else-like type that resolves depending on whether the given type is `unknown`. * @see {@link https://github.com/sindresorhus/type-fest/blob/main/source/if-unknown.d.ts Source} * * @internal */ export type IfUnknown<T, TypeIfUnknown, TypeIfNotUnknown> = unknown extends T // `T` can be `unknown` or `any` ? [T] extends [null] // `any` can be `null`, but `unknown` can't be ? TypeIfNotUnknown : TypeIfUnknown : TypeIfNotUnknown /** * When a type is resolves to `unknown`, fallback to a different type. * * @template T - Type to be checked. * @template FallbackTo - Type to fallback to if `T` resolves to `unknown`. * * @internal */ export type FallbackIfUnknown<T, FallbackTo> = IfUnknown<T, FallbackTo, T> /** * * ----------------------------------------------------------------------------- * ----------------------------------------------------------------------------- * * Type Expansion Utilities * * ----------------------------------------------------------------------------- * ----------------------------------------------------------------------------- * */ /** * Check whether `U` contains `U1`. * @see {@link https://millsp.github.io/ts-toolbelt/modules/union_has.html Source} * * @internal */ export type Has<U, U1> = [U1] extends [U] ? 1 : 0 /** * @internal */ export type Boolean2 = 0 | 1 /** * @internal */ export type If2<B extends Boolean2, Then, Else = never> = B extends 1 ? Then : Else /** * @internal */ export type BuiltIn = | Function | Error | Date | { readonly [Symbol.toStringTag]: string } | RegExp | Generator /** * Expand an item a single level. * @see {@link https://stackoverflow.com/a/69288824/62937 Source} * * @internal */ export type Expand<T> = T extends (...args: infer A) => infer R ? (...args: Expand<A>) => Expand<R> : T extends infer O ? { [K in keyof O]: O[K] } : never /** * Expand an item recursively. * @see {@link https://stackoverflow.com/a/69288824/62937 Source} * * @internal */ export type ExpandRecursively<T> = T extends (...args: infer A) => infer R ? (...args: ExpandRecursively<A>) => ExpandRecursively<R> : T extends object ? T extends infer O ? { [K in keyof O]: ExpandRecursively<O[K]> } : never : T /** * @internal */ export type Identity<T> = T /** * Another form of type value expansion * @see {@link https://github.com/microsoft/TypeScript/issues/35247 Source} * * @internal */ export type Mapped<T> = Identity<{ [k in keyof T]: T[k] }> /** * This utility type is primarily used to expand a function type in order to * improve its visual display in hover previews within IDEs. * * __Disclaimer:__ Functions expanded using this type will not display their * original JSDoc information in hover previews. * * @template FunctionType - The type of the function to be expanded. * * @internal */ export type ExpandFunction<FunctionType extends AnyFunction> = FunctionType extends FunctionType ? (...args: Parameters<FunctionType>) => ReturnType<FunctionType> : never /** * Useful to flatten the type output to improve type hints shown in editors. * And also to transform an interface into a type to aide with assignability. * @see {@link https://github.com/sindresorhus/type-fest/blob/main/source/simplify.d.ts Source} * * @internal */ export type Simplify<T> = T extends AnyFunction ? T : { [KeyType in keyof T]: T[KeyType] } & AnyNonNullishValue /** * Fully expand a type, deeply * @see {@link https://github.com/millsp/ts-toolbelt Any.Compute} * * @internal */ export type ComputeDeep<A, Seen = never> = A extends BuiltIn ? A : If2< Has<Seen, A>, A, A extends any[] ? A extends Record<PropertyKey, any>[] ? ({ [K in keyof A[number]]: ComputeDeep<A[number][K], A | Seen> } & unknown)[] : A : A extends readonly any[] ? A extends readonly Record<PropertyKey, any>[] ? readonly ({ [K in keyof A[number]]: ComputeDeep<A[number][K], A | Seen> } & unknown)[] : A : { [K in keyof A]: ComputeDeep<A[K], A | Seen> } & unknown >