import type { $OmitArrayKeys, $PreservedValue, $Dictionary, $SpecialObject, $StringKeyPathToRecord, $NoInfer, $Prune, $Turtles, } from './helpers.js'; import type { TypeOptions, Namespace, FlatNamespace, DefaultNamespace, TOptions, TOptionsBase, } from './options.js'; /** @todo consider to replace {} with Record */ // Type Options type _ReturnObjects = TypeOptions['returnObjects']; type _ReturnEmptyString = TypeOptions['returnEmptyString']; type _ReturnNull = TypeOptions['returnNull']; type _KeySeparator = TypeOptions['keySeparator']; type _NsSeparator = TypeOptions['nsSeparator']; type _PluralSeparator = TypeOptions['pluralSeparator']; type _ContextSeparator = TypeOptions['contextSeparator']; type _FallbackNamespace = TypeOptions['fallbackNS']; type _Resources = TypeOptions['resources']; type _CompatibilityJSON = TypeOptions['compatibilityJSON']; type _InterpolationPrefix = TypeOptions['interpolationPrefix']; type _InterpolationSuffix = TypeOptions['interpolationSuffix']; type _UnescapePrefix = TypeOptions['unescapePrefix']; type _UnescapeSuffix = TypeOptions['unescapeSuffix']; type _StrictKeyChecks = TypeOptions['strictKeyChecks']; type _EnableSelector = TypeOptions['enableSelector']; type _ParseInterpolation = TypeOptions['parseInterpolation']; type _InterpolationFormatTypeMap = TypeOptions['interpolationFormatTypeMap']; type $IsResourcesDefined = [keyof _Resources] extends [never] ? false : true; type $ValueIfResourcesDefined = $IsResourcesDefined extends true ? Value : Fallback; type $FirstNamespace = Ns extends readonly any[] ? Ns[0] : Ns; type Resources = $ValueIfResourcesDefined<_Resources, $Dictionary>; type PluralSuffix = 'zero' | 'one' | 'two' | 'few' | 'many' | 'other'; type WithOrWithoutPlural = _CompatibilityJSON extends 'v4' ? Key extends `${infer KeyWithoutOrdinalPlural}${_PluralSeparator}ordinal${_PluralSeparator}${PluralSuffix}` ? KeyWithoutOrdinalPlural | Key : Key extends `${infer KeyWithoutPlural}${_PluralSeparator}${PluralSuffix}` ? KeyWithoutPlural | Key : Key : Key; type JoinKeys = `${K1 & string}${_KeySeparator}${K2 & string}`; type AppendNamespace = `${Ns & string}${_NsSeparator}${Keys & string}`; type TrimSpaces = T extends `${infer Char}${infer Rest}` ? Char extends ' ' ? TrimSpaces : TrimSpaces : T extends '' ? Acc : never; interface Branded { $TFunctionBrand: $IsResourcesDefined extends true ? `${Ns extends readonly any[] ? Ns[0] : Ns}` : never; } /** **************************************************** * Build all keys and key prefixes based on Resources * ***************************************************** */ type KeysBuilderWithReturnObjects = Key extends keyof Res ? Res[Key] extends $Dictionary | readonly unknown[] ? | JoinKeys>> | JoinKeys> : never : never; type KeysBuilderWithoutReturnObjects> = Key extends keyof Res ? Res[Key] extends $Dictionary | readonly unknown[] ? JoinKeys> : Key : never; type KeysBuilder = $IsResourcesDefined extends true ? WithReturnObjects extends true ? keyof Res | KeysBuilderWithReturnObjects : KeysBuilderWithoutReturnObjects : string; type KeysWithReturnObjects = { [Ns in FlatNamespace]: WithOrWithoutPlural>; }; type KeysWithoutReturnObjects = { [Ns in FlatNamespace]: WithOrWithoutPlural>; }; type ResourceKeys = WithReturnObjects extends true ? KeysWithReturnObjects : KeysWithoutReturnObjects; /** ********************************************************************** * Parse t function keys based on the namespace, options and key prefix * *********************************************************************** */ export type KeysByTOptions = TOpt['returnObjects'] extends true ? ResourceKeys : ResourceKeys; export type NsByTOptions = TOpt['ns'] extends Namespace ? TOpt['ns'] : Ns; type ParseKeysByKeyPrefix = KPrefix extends string ? Keys extends `${KPrefix}${_KeySeparator}${infer Key}` ? Key : never : Keys; type ParseKeysByNamespaces = Ns extends readonly (infer UnionNsps)[] ? UnionNsps extends keyof Keys ? AppendNamespace : never : never; type ParseKeysByFallbackNs = _FallbackNamespace extends false ? never : _FallbackNamespace extends (infer UnionFallbackNs extends string)[] ? Keys[UnionFallbackNs] : Keys[_FallbackNamespace & string]; export type FilterKeysByContext = Context extends string ? Keys extends | `${infer Prefix}${_ContextSeparator}${Context}${_PluralSeparator}${PluralSuffix}` | `${infer Prefix}${_ContextSeparator}${Context}` ? Prefix : never : Keys; export type ParseKeys< Ns extends Namespace = DefaultNamespace, TOpt extends TOptions = {}, KPrefix = undefined, Keys extends $Dictionary = KeysByTOptions, ActualNS extends Namespace = NsByTOptions, Context extends TOpt['context'] = TOpt['context'], > = $IsResourcesDefined extends true ? FilterKeysByContext< | ParseKeysByKeyPrefix], KPrefix> | ParseKeysByNamespaces | ParseKeysByFallbackNs, Context > : string; /** ******************************************************* * Parse t function return type and interpolation values * ******************************************************** */ type ParseActualValue = Ret extends `${_UnescapePrefix}${infer ActualValue}${_UnescapeSuffix}` ? TrimSpaces : Ret; /** Parses interpolation entries as `[variableName, formatSpecifier | never]` tuples. */ type ParseInterpolationEntries = [_ParseInterpolation] extends [false] ? never : Ret extends `${string}${_InterpolationPrefix}${infer Value}${_InterpolationSuffix}${infer Rest}` ? | (Value extends `${infer ActualValue},${infer Format}` ? [ParseActualValue, TrimSpaces] : [ParseActualValue, never]) | ParseInterpolationEntries : never; /** Extracts just the variable names (kept for backward compat with ParseInterpolationValues usage). */ type ParseInterpolationValues = ParseInterpolationEntries[0]; /** Built-in i18next formatter name → value type mapping. */ type _BuiltInFormatTypeMap = { number: number; currency: number; datetime: Date; relativetime: number; list: readonly string[]; }; /** Strips inline formatting options, e.g. `currency(EUR)` → `currency`. */ type _StripFormatOptions = F extends `${infer Name}(${string})` ? Name : F; /** Resolves the type for a single interpolation entry based on name and format. */ type _ResolveEntryType = [Format] extends [never] ? Name extends 'count' ? number : string | number : Format extends keyof _InterpolationFormatTypeMap ? _InterpolationFormatTypeMap[Format] : _StripFormatOptions extends keyof _InterpolationFormatTypeMap ? _InterpolationFormatTypeMap[_StripFormatOptions & keyof _InterpolationFormatTypeMap] : Format extends keyof _BuiltInFormatTypeMap ? _BuiltInFormatTypeMap[Format] : _StripFormatOptions extends keyof _BuiltInFormatTypeMap ? _BuiltInFormatTypeMap[_StripFormatOptions & keyof _BuiltInFormatTypeMap] : string; /** Local union-to-intersection (not exported from helpers). */ type _UnionToIntersection = (T extends unknown ? (k: T) => void : never) extends ( k: infer I, ) => void ? I : never; /** Builds a per-entry typed record from parsed interpolation entries and intersects them. */ type _InterpolationMapFromEntries = _UnionToIntersection< E extends [infer Name extends string, infer Format] ? $StringKeyPathToRecord> : never >; export type InterpolationMap = $PreservedValue< _InterpolationMapFromEntries>, Record >; type ParseTReturnPlural< Res, Key, KeyWithPlural = `${Key & string}${_PluralSeparator}${PluralSuffix}`, > = Res[(KeyWithPlural | Key) & keyof Res]; type ParseTReturnPluralOrdinal< Res, Key, KeyWithOrdinalPlural = `${Key & string}${_PluralSeparator}ordinal${_PluralSeparator}${PluralSuffix}`, > = Res[(KeyWithOrdinalPlural | Key) & keyof Res]; type ParseTReturnWithFallback = Val extends '' ? _ReturnEmptyString extends true ? '' : Key : Val extends null ? _ReturnNull extends true ? null : Key : Val; type ParseTReturn = ParseTReturnWithFallback< Key, Key extends `${infer K1}${_KeySeparator}${infer RestKey}` ? ParseTReturn : // Process plurals only if count is provided inside options TOpt['count'] extends number ? TOpt['ordinal'] extends boolean ? ParseTReturnPluralOrdinal : ParseTReturnPlural : // otherwise access plain key without adding plural and ordinal suffixes Res extends readonly unknown[] ? Key extends `${infer NKey extends number}` ? Res[NKey] : never : Res[Key & keyof Res] >; type TReturnOptionalNull = _ReturnNull extends true ? null : never; type TReturnOptionalObjects = _ReturnObjects extends true ? $SpecialObject | string : TOpt['returnObjects'] extends true ? $SpecialObject : string; type DefaultTReturn = | TReturnOptionalObjects | TReturnOptionalNull; export type KeyWithContext = TOpt['context'] extends string ? `${Key & string}${_ContextSeparator}${TOpt['context']}` : Key; export type ContextOfKey< Key extends string, Ns extends Namespace = DefaultNamespace, TOpt extends TOptions = {}, KPrefix = undefined, Keys extends $Dictionary = KeysByTOptions, ActualNS extends Namespace = NsByTOptions, ActualKeys = | ParseKeysByKeyPrefix], KPrefix> | ParseKeysByNamespaces | ParseKeysByFallbackNs, > = $IsResourcesDefined extends true ? Key extends ActualKeys ? string : ActualKeys extends | `${Key}${_ContextSeparator}${infer Context}${_PluralSeparator}${PluralSuffix}` | `${Key}${_ContextSeparator}${infer Context}` ? Context : never : string; // helper that maps the configured fallbackNS value to the matching resources slice type FallbackResourcesOf = FallbackNS extends readonly (infer FN)[] ? R[FN & keyof R] : [FallbackNS] extends [false] ? never : R[Extract & keyof R]; /* reuse the parse helpers as top-level aliases (no nested type declarations) */ type _PrimaryParse< ActualKey, PrimaryNS extends keyof Resources, TOpt extends TOptions, > = ParseTReturn; type _FallbackParse = [ FallbackResourcesOf, ] extends [never] ? never : ParseTReturn, TOpt>; export type TFunctionReturn< Ns extends Namespace, Key, TOpt extends TOptions, ActualNS extends Namespace = NsByTOptions, ActualKey = KeyWithContext, > = $IsResourcesDefined extends true ? ActualKey extends `${infer Nsp}${_NsSeparator}${infer RestKey}` ? ParseTReturn : $FirstNamespace extends infer PrimaryNS ? [PrimaryNS] extends [keyof Resources] ? [_PrimaryParse] extends [never] ? [_FallbackParse] extends [never] ? DefaultTReturn : _FallbackParse : _PrimaryParse : never : never : DefaultTReturn; export type TFunctionDetailedResult = { /** * The plain used key */ usedKey: string; /** * The translation result. */ res: T; /** * The key with context / plural */ exactUsedKey: string; /** * The used language for this translation. */ usedLng: string; /** * The used namespace for this translation. */ usedNS: string; /** * The parameters used for interpolation. */ usedParams: InterpolationMap & { count?: TOpt['count'] }; }; type TFunctionProcessReturnValue = Ret extends string | $SpecialObject | null ? Ret : [DefaultValue] extends [never] ? Ret : DefaultValue; type TFunctionReturnOptionalDetails = TOpt['returnDetails'] extends true ? TFunctionDetailedResult : Ret; type AppendKeyPrefix = KPrefix extends string ? `${KPrefix}${_KeySeparator}${Key & string}` : Key; /** * Resolves the effective key prefix by preferring a per-call `keyPrefix` from * options over the interface-level `KPrefix` (set via getFixedT's 3rd argument). */ type EffectiveKPrefix = TOpt extends { keyPrefix: infer OptKP extends string } ? OptKP : KPrefix; /** ************************ * T function declaration * ************************* */ interface TFunctionStrict< Ns extends Namespace = DefaultNamespace, KPrefix = undefined, > extends Branded { < const Key extends ParseKeys> | TemplateStringsArray, const TOpt extends TOptions, Ret extends TFunctionReturn>, TOpt>, >( key: Key | Key[], options?: TOpt & InterpolationMap & { context?: Key extends string ? unknown extends TOpt['context'] ? TOpt['context'] : ContextOfKey> : never; }, ): TFunctionReturnOptionalDetails, never>, TOpt>; < const Key extends ParseKeys> | TemplateStringsArray, const TOpt extends TOptions, Ret extends TFunctionReturn>, TOpt>, >( key: Key | Key[], defaultValue: string, options?: TOpt & InterpolationMap & { context?: Key extends string ? unknown extends TOpt['context'] ? TOpt['context'] : ContextOfKey> : never; }, ): TFunctionReturnOptionalDetails, never>, TOpt>; } interface TFunctionNonStrict< Ns extends Namespace = DefaultNamespace, KPrefix = undefined, > extends Branded { < const Key extends ParseKeys> | TemplateStringsArray, const TOpt extends TOptions, Ret extends TFunctionReturn>, TOpt>, const ActualOptions extends Omit & InterpolationMap & { context?: Key extends string ? unknown extends TOpt['context'] ? TOpt['context'] : ContextOfKey> : never; } = TOpt & InterpolationMap & { context?: Key extends string ? unknown extends TOpt['context'] ? TOpt['context'] : ContextOfKey> : never; }, DefaultValue extends string = never, >( ...args: | [key: Key | Key[], options?: ActualOptions] | [key: string | string[], options: TOpt & $Dictionary & { defaultValue: DefaultValue }] | [key: string | string[], defaultValue: DefaultValue, options?: TOpt & $Dictionary] ): TFunctionReturnOptionalDetails, DefaultValue>, TOpt>; } type TFunctionSignature< Ns extends Namespace = DefaultNamespace, KPrefix = undefined, > = _EnableSelector extends true | 'optimize' | 'strict' ? TFunctionSelector> : _StrictKeyChecks extends true ? TFunctionStrict : TFunctionNonStrict; export interface TFunction< Ns extends Namespace = DefaultNamespace, KPrefix = undefined, > extends TFunctionSignature {} export type KeyPrefix = ResourceKeys[$FirstNamespace] | undefined; /** * Strict variant: every namespace (primary included) is exposed under its own * key on `$`. The flattened-primary form (`Resources[Ns[0]] &`) is dropped, so * `$.foo` no longer typechecks when `foo` is a key in the primary namespace — * callers must write `$.primary.foo`. Applied uniformly to single- and * multi-ns hooks under `enableSelector: 'strict'`. */ type _NsResourceStrict = Ns extends readonly any[] ? PickNamespaces : PickNamespaces; /** The raw (unfiltered) resource object for a given namespace. */ export type NsResource = [_EnableSelector] extends ['strict'] ? _NsResourceStrict : Ns extends readonly [keyof Resources, any, ...any] ? Resources[Ns[0]] & PickNamespaces : Resources[$FirstNamespace]; /** A selector function that can be used as `keyPrefix` to scope `t()` to a sub-tree of the resource. */ export type KeyPrefixSelector = (src: NsResource) => object; /** * The type of a selector function accepted by `t()` for a given namespace and optional key prefix. * Use this instead of `Parameters>[0]` for a stable, readable type. * * @example * ```ts * import type { SelectorParam } from 'i18next'; * * interface CmpProps { * i18nKey: SelectorParam<'myNamespace'>; * } * ``` */ export type SelectorParam = ( src: Select, undefined>, ) => string; /// ////////////// /// /// ↆ selector ↆ /// /// ////////////// /// declare const $PluralBrand: unique symbol; /** Marks a value as coming from a plural-form key, requiring `count` in the selector options. */ type PluralValue = T & { readonly [$PluralBrand]: typeof $PluralBrand }; declare const $SelectorKeyBrand: unique symbol; /** * A branded string produced by {@link keyFromSelector}. * Can be passed directly to `t()` when the selector API is enabled. */ export type SelectorKey = string & { readonly [$SelectorKeyBrand]: typeof $SelectorKeyBrand }; /** * Type-safe signature for {@link keyFromSelector}. * Constrains the selector callback against the default namespace's resources * (same source that `t()` uses for selectors without explicit `ns`). * When resources are not defined, accepts any selector. */ export type KeyFromSelectorFn = < Ns extends Namespace = DefaultNamespace, KPrefix extends KeyPrefix = undefined, >( selector: (src: Select, undefined>) => any, opts?: { ns?: Ns; keyPrefix?: KPrefix }, ) => SelectorKey; /** Recursively strips the {@link PluralValue} brand from a type (handles nested objects for `returnObjects`). */ type DeepUnwrapPlural = T extends PluralValue ? U : T extends readonly any[] ? { [I in keyof T]: DeepUnwrapPlural } : T extends object ? { [K in keyof T]: DeepUnwrapPlural } : T; type NsArg = Ns[number] | readonly Ns[number][]; interface TFunctionSelector extends Branded { // ── Pre-computed key(s) from keyFromSelector ──────────────────────────────── // Accepts a branded `SelectorKey` (or array of them) produced by `keyFromSelector`. // Return-type precision is traded for the flexibility of decoupled key creation. // Placed first so that the more general selector overloads (below) are last — // TypeScript's `Parameters<>` utility resolves against the last call signature. = SelectorOptions>( key: SelectorKey | SelectorKey[], ...args: [options?: Opts & $Dictionary] ): DefaultTReturn; // ── Selector(s) with explicit `ns` ─────────────────────────────────────────── < Target extends ConstrainTarget, const NewNs extends NsArg & Namespace, const Opts extends SelectorOptions, NewSrc extends GetSource, >( selector: | SelectorFn, Opts> | readonly SelectorFn, Opts>[], options: Opts & InterpolationMap & { ns: NewNs }, ): SelectorReturn; // ── Array of selectors with default `ns` ───────────────────────────────────── // Captures the selector tuple as `const Fns` so TypeScript preserves each // element's exact return type. The union of return types is then extracted // via a distributive `infer`, which correctly handles mixed PluralValue and // plain-string callbacks that would otherwise cause TypeScript to "lock in" the // type from the first element. < const Fns extends readonly ((src: Select) => string | object)[], const Opts extends SelectorOptions = SelectorOptions, >( selectors: Fns, options?: Opts & InterpolationMap< DeepUnwrapPlural infer R ? R : never> >, ): SelectorReturn infer R ? R : never, Opts>; // ── Single selector with context — bypasses count enforcement ──────────────── // When `context` is present in options, `Target` is derived from the // context-filtered source (third mapped type of FilterKeys), which does NOT // apply the PluralValue brand. A separate overload avoids the circular // inference that would otherwise occur when `Opts['context']` sits inside the // conditional rest tuple of the overload below. < Target extends ConstrainTarget, const NewNs extends NsArg = Ns[number], const Opts extends SelectorOptions & { context: string } = SelectorOptions & { context: string; }, >( selector: SelectorFn, Opts>, options: Opts & InterpolationMap, ): SelectorReturn; // ── Single selector with defaultValue — preserves literal type of DV ──────── // `const Opts` loses literal precision for `defaultValue` when inferred // through a conditional rest tuple (TypeScript limitation). This dedicated // overload captures `DV` from a regular (non-conditional) parameter position, // preserving its literal type. Count enforcement for plural keys is achieved // via a conditional intersection on the options parameter. < const Fn extends (src: Select) => ConstrainTarget, const DV extends string, const Opts extends SelectorOptions = SelectorOptions, >( selector: Fn, options: Opts & { defaultValue: DV } & (ReturnType extends PluralValue ? { count: number } : {}) & InterpolationMap>>, ): SelectorReturn, Opts, DV>; // ── Single selector without context — enforces count for plural keys ────────── // Uses `const Fn` to capture the selector's exact return type independently of // `Opts`, breaking the circular dependency between `Target` inference and the // conditional rest tuple. `ReturnType` drives both the plural check and // the return type; `Opts` is inferred later from the resolved rest args. < const Fn extends (src: Select) => ConstrainTarget, const Opts extends SelectorOptions = SelectorOptions, >( selector: Fn, ...args: ReturnType extends PluralValue ? [options: Opts & { count: number } & InterpolationMap>>] : [options?: Opts & InterpolationMap>] ): SelectorReturn, Opts>; } interface SelectorOptions extends Omit, $Dictionary { ns?: Ns; } type SelectorReturn< Target, Opts extends { defaultValue?: unknown; returnObjects?: boolean }, DV = Opts['defaultValue'], > = $IsResourcesDefined extends true ? TFunctionReturnOptionalDetails, DV>, Opts> : DefaultTReturn; interface SelectorFn> { (translations: Select): Target; } type ApplyKeyPrefix< T extends [any], KPrefix, > = KPrefix extends `${infer Head}${_KeySeparator}${infer Tail}` ? ApplyKeyPrefix<[T[0][Head]], Tail> : T[0][KPrefix & string]; type ApplyTarget< Target, Opts extends { returnObjects?: unknown }, > = Opts['returnObjects'] extends true ? unknown : Target; type ConstrainTarget> = _ReturnObjects extends true ? unknown : Opts['returnObjects'] extends true ? unknown : $IsResourcesDefined extends false ? unknown : string; type ProcessReturnValue = $Turtles extends Target ? string : [DefaultValue] extends [never] ? Target : unknown extends DefaultValue ? Target : Target | DefaultValue; type PickNamespaces = { [P in K as P extends keyof T ? P : never]: T[P & keyof T]; }; /** Extracts the sub-tree returned by a selector function used as keyPrefix. */ type SelectorReturnSource = KPrefix extends (...args: any[]) => infer R ? R extends object ? R : Fallback : Fallback; type GetSource> = KPrefix extends ( ...args: any[] ) => any ? SelectorReturnSource : KPrefix extends keyof Res ? Res[KPrefix] : undefined extends KPrefix ? Res : ApplyKeyPrefix<[Res], KPrefix>; type Select = $IsResourcesDefined extends false ? $Turtles : [_EnableSelector] extends ['optimize'] ? T : FilterKeys; type _HasContextVariant = [ keyof T & ( | `${K}${_ContextSeparator}${Context & string}` | `${K}${_ContextSeparator}${Context & string}${_PluralSeparator}${PluralSuffix}` ), ] extends [never] ? false : true; /** Checks whether key K has **any** context variant in T (excluding pure plural suffixes). */ type _IsContextualKey = [ Exclude< keyof T & `${K}${_ContextSeparator}${string}`, | `${K}${_PluralSeparator}${PluralSuffix}` | `${K}${_PluralSeparator}ordinal${_PluralSeparator}${PluralSuffix}` >, ] extends [never] ? false : true; type FilterKeys = never | T extends readonly any[] ? { [I in keyof T]: FilterKeys } : $Prune< { // Mapped type 1: object-valued keys (recurse) + plain leaf keys (non-plural, non-context) [K in keyof T as T[K] extends object ? K : [Context] extends [string] ? K extends | `${string}${_ContextSeparator}${Context}` | `${string}${_ContextSeparator}${Context}${_PluralSeparator}${PluralSuffix}` ? never // context keys handled by mapped type 3 : K extends `${string}${_PluralSeparator}${PluralSuffix}` ? never // plural keys handled by mapped type 2 : K extends string ? _HasContextVariant extends true ? never // context variant exists, drop base key (type 3 handles it) : _IsContextualKey extends true ? never // key has context variants but not for this context : K // no context variants at all, keep base key : K : K extends `${string}${_PluralSeparator}${PluralSuffix}` ? never : K]: T[K] extends object ? FilterKeys : T[K]; } & { // Mapped type 2: plural collapsing (active regardless of context) [K in keyof T as T[K] extends object ? never : [Context] extends [string] ? K extends | `${string}${_ContextSeparator}${Context}` | `${string}${_ContextSeparator}${Context}${_PluralSeparator}${PluralSuffix}` ? never // context keys handled by mapped type 3 : K extends | `${infer Prefix}${_PluralSeparator}${PluralSuffix}` | `${infer Prefix}${_PluralSeparator}ordinal${_PluralSeparator}${PluralSuffix}` ? Prefix : never : K extends | `${infer Prefix}${_PluralSeparator}${PluralSuffix}` | `${infer Prefix}${_PluralSeparator}ordinal${_PluralSeparator}${PluralSuffix}` ? Prefix : never]: T[K] extends object ? FilterKeys : PluralValue; } & { // Mapped type 3: context key collapsing [K in keyof T as T[K] extends object ? never : [Context] extends [string] ? K extends | `${infer Prefix}${_ContextSeparator}${Context}` | `${infer Prefix}${_ContextSeparator}${Context}${_PluralSeparator}${PluralSuffix}` ? Prefix : never : never]: T[K] extends object ? FilterKeys : T[K]; } >;