From 2f57070f7976e737853775ef879af4038bc8455b Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Fri, 24 Nov 2023 15:57:13 +0100 Subject: [PATCH] core-plugin-api: refactor and more comments for FlattenedMessages Signed-off-by: Patrik Oldsberg --- .../src/translation/TranslationRef.ts | 43 ++++++++++++------- 1 file changed, 28 insertions(+), 15 deletions(-) diff --git a/packages/core-plugin-api/src/translation/TranslationRef.ts b/packages/core-plugin-api/src/translation/TranslationRef.ts index 5711273915..c2ac7a5ce3 100644 --- a/packages/core-plugin-api/src/translation/TranslationRef.ts +++ b/packages/core-plugin-api/src/translation/TranslationRef.ts @@ -42,21 +42,34 @@ type AnyNestedMessages = { [key in string]: AnyNestedMessages | string }; * * @ignore */ -type FlattenedMessages = { - [K in keyof T]: ( - x: T[K] extends infer V - ? V extends AnyNestedMessages - ? FlattenedMessages extends infer FV - ? { - [P in keyof FV as `${K & string}.${P & string}`]: FV[P]; - } - : never - : Pick - : never, - ) => void; -} extends Record void> - ? { [K in keyof O]: O[K] } - : never; +type FlattenedMessages = + // Flatten out object keys into a union structure of objects, e.g. { a: 'a', b: 'b' } -> { a: 'a' } | { b: 'b' } + // Any nested object will be flattened into the individual unions, e.g. { a: 'a', b: { x: 'x', y: 'y' } } -> { a: 'a' } | { 'b.x': 'x', 'b.y': 'y' } + // We create this structure by first nesting the desired union types into the original object, and + // then extract them by indexing with `keyof TMessages` to form the union. + // Throughout this the objects are wrapped up in a function parameter, which allows us to have the + // final step of flipping this unions around to an intersection by inferring the function parameter. + { + [TKey in keyof TMessages]: ( + _: TMessages[TKey] extends infer TValue // "local variable" for the value + ? TValue extends AnyNestedMessages + ? FlattenedMessages extends infer TNested // Recurse into nested messages, "local variable" for the result + ? { + [TNestedKey in keyof TNested as `${TKey & string}.${TNestedKey & + string}`]: TNested[TNestedKey]; + } + : never + : { [_ in TKey]: TValue } // Primitive object values are passed through with the same key + : never, + ) => void; + // The `[keyof TMessages]` extracts the object values union from our flattened structure, still wrapped up in function parameters. + // The `extends (_: infer TIntersection) => void` flips the union to an intersection, at which point we have the correct type. + }[keyof TMessages] extends (_: infer TIntersection) => void + ? // This object mapping just expands similar to the Expand<> utility type, providing nicer type hints + { + readonly [TExpandKey in keyof TIntersection]: TIntersection[TExpandKey]; + } + : never; /** @internal */ export interface InternalTranslationRef<