type Flatten = T extends object ? { [P in keyof T]: T[P]; } : T; type EmptyObject = Record; type OtherString = string & { __type: 'other'; }; interface GetICUArgsOptions { ProvidedArgs?: string; ICUNumberArgument?: unknown; ICUDateArgument?: unknown; ICUArgument?: unknown; } type Whitespace = ' ' | '\t' | '\n' | '\r'; /** Remove leading and tailing whitespace */ type Trim = T extends `${Whitespace}${infer Rest}` ? Trim : T extends `${infer Rest}${Whitespace}` ? Trim : T extends string ? T : never; /** Returns an array of top level blocks */ type FindBlocks = Text extends `${string}{${infer Right}` ? ReadBlock<'', Right, ''> extends [infer Block, infer Tail] ? [Block, ...FindBlocks] : [EmptyObject] : []; /** Find blocks for each tuple entry */ type TupleFindBlocks = T extends readonly [infer First, ...infer Rest] ? [...FindBlocks, ...TupleFindBlocks] : []; /** Read tail until the currently open block is closed. Return the block content and rest of tail */ type ReadBlock = Tail extends `${infer L1}}${infer R1}` ? L1 extends `${infer L2}{${infer R2}` ? ReadBlock<`${Block}${L2}{`, `${R2}}${R1}`, `${Depth}+`> : Depth extends `+${infer Rest}` ? ReadBlock<`${Block}${L1}}`, R1, Rest> : [`${Block}${L1}`, R1] : []; /** Parse block, return variables with types and recursively find nested blocks within */ type ParseBlock = Block extends `${infer Name},${infer Format},${infer Rest}` ? Trim extends 'select' ? SelectOptions, Trim, Opts> : { [K in Trim]: VariableType, Opts>; } & TupleParseBlock>, Opts> : Block extends `${infer Name},${infer Format}` ? { [K in Trim]: VariableType, Opts>; } : { [K in Trim]: Opts['ICUArgument']; }; /** Parse block for each tuple entry */ type TupleParseBlock = T extends readonly [infer First, ...infer Rest] ? ParseBlock & TupleParseBlock : EmptyObject; type VariableType = T extends 'number' | 'plural' | 'selectordinal' ? Opts['ICUNumberArgument'] : T extends 'date' | 'time' ? Opts['ICUDateArgument'] : Opts['ICUArgument']; type SelectOptions = KeepAndMerge>; type ParseSelectBlock = Rest extends `${infer Left}{${infer Right}` ? ReadBlock<'', Right, ''> extends [infer Block, infer Tail] ? ({ [K in Name]: HandleOther>; } & TupleParseBlock, Opts>) | ParseSelectBlock : never : never; type HandleOther = 'other' extends T ? Exclude | OtherString : T; type KeepAndMerge = T | MergeTypeUnion; type KeysFromUnion = T extends T ? keyof T : never; type SimpleTypeMerge = T extends { [k in K]?: any; } ? (T[K] extends OtherString ? string & {} : T[K]) : never; type MergeTypeUnion = { [k in KeysFromUnion]: SimpleTypeMerge; }; type EscapeLike = `'${'{' | '}' | '<' | '>'}`; type StripEscapes = T extends `${infer Left}''${infer Right}` ? `${Left}${Right}` : T extends `${infer Start}${EscapeLike}${string}'${infer End}` ? `${Start}${StripEscapes}` : T extends `${infer Start}${EscapeLike}${string}` ? Start : T; type TupleStripEscapes = T extends readonly [infer First, ...infer Rest] ? [StripEscapes, ...TupleStripEscapes] : []; type MakeProvidedOptional = { [K in keyof T as K extends ProvidedArgs ? never : K]: T[K]; } & { [K in ProvidedArgs & keyof T]?: T[K]; }; /** Calculates an object type with all variables and their types in the given ICU format string */ type GetICUArgs = Flatten> : FindBlocks>, TOptions>, 'ProvidedArgs' extends keyof TOptions ? TOptions['ProvidedArgs'] : never>>; export type { GetICUArgs, GetICUArgsOptions };