const TRANSLATION_KEY_PLACEHOLDER = Symbol('TRANSLATION_KEY_PLACEHOLDER'); type TranslationKeyPlaceholder = typeof TRANSLATION_KEY_PLACEHOLDER; export interface TranslationStructure { [key: string]: TranslationStructure | TranslationKeyPlaceholder; } export type TranslationKeysOf = { [P in keyof T]: T[P] extends TranslationStructure ? TranslationKeysOf : string }; export function defineTranslationKeys( translationStructure: (t: TranslationKeyPlaceholder) => T ): TranslationKeysOf { return generateTranslationKeys(translationStructure(TRANSLATION_KEY_PLACEHOLDER)); } function generateTranslationKeys(structure: U, prefix?: string): TranslationKeysOf { return Object.keys(structure).reduce>>( (keys, key) => { const value = structure[key]; const path = prefix === undefined ? key : `${prefix}.${key}`; return { ...keys, [key]: value === TRANSLATION_KEY_PLACEHOLDER ? path : generateTranslationKeys(value, path) }; }, {} ) as TranslationKeysOf; }