chore: intermidiate step of a new way of generating object type.
diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/export/ExportModel.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/export/ExportModel.kt
index 5b72f66..39ccba1 100644
--- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/export/ExportModel.kt
+++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/export/ExportModel.kt
@@ -20,7 +20,8 @@
 
 class ExportedNamespace(
     val name: String,
-    val declarations: List<ExportedDeclaration>
+    val declarations: List<ExportedDeclaration>,
+    val isSynthetic: Boolean = false
 ) : ExportedDeclaration()
 
 data class ExportedFunction(
@@ -59,7 +60,6 @@
     val isField: Boolean,
     val irGetter: IrFunction?,
     val irSetter: IrFunction?,
-    val exportedObject: ExportedClass? = null,
 ) : ExportedDeclaration()
 
 
@@ -78,6 +78,16 @@
     val ir: IrClass
 ) : ExportedDeclaration()
 
+data class ExportedObject(
+    val name: String,
+    val superClass: ExportedType? = null,
+    val superInterfaces: List<ExportedType> = emptyList(),
+    val members: List<ExportedDeclaration>,
+    val nestedClasses: List<ExportedClass>,
+    val ir: IrClass,
+    val irGetter: IrFunction,
+) : ExportedDeclaration()
+
 class ExportedParameter(
     val name: String,
     val type: ExportedType,
diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/export/ExportModelGenerator.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/export/ExportModelGenerator.kt
index 379acfb..9b86bf8 100644
--- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/export/ExportModelGenerator.kt
+++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/export/ExportModelGenerator.kt
@@ -353,43 +353,29 @@
 
         val name = klass.getExportedIdentifier()
 
-        val exportedClass = ExportedClass(
-            name = name,
-            isInterface = klass.isInterface,
-            isAbstract = klass.modality == Modality.ABSTRACT,
-            superClass = superType,
-            superInterfaces = superInterfaces,
-            typeParameters = typeParameters,
-            members = members,
-            nestedClasses = nestedClasses,
-            ir = klass
-        )
-
-        if (klass.kind == ClassKind.OBJECT) {
-            var t: ExportedType = ExportedType.InlineInterfaceType(members + nestedClasses)
-            if (superType != null)
-                t = ExportedType.IntersectionType(t, superType)
-
-            for (superInterface in superInterfaces) {
-                t = ExportedType.IntersectionType(t, superInterface)
-            }
-
-            return ExportedProperty(
+        return if (klass.kind == ClassKind.OBJECT) {
+            ExportedObject(
                 name = name,
-                type = t,
-                mutable = false,
-                isMember = klass.parent is IrClass,
-                isStatic = !klass.isInner,
-                isAbstract = false,
-                isProtected = klass.visibility == DescriptorVisibilities.PROTECTED,
-                irGetter = context.mapping.objectToGetInstanceFunction[klass]!!,
-                irSetter = null,
-                exportedObject = exportedClass,
-                isField = false,
+                superClass = superType,
+                superInterfaces = superInterfaces,
+                members = members,
+                nestedClasses = nestedClasses,
+                ir = klass,
+                irGetter = context.mapping.objectToGetInstanceFunction[klass]!!
+            )
+        } else {
+            ExportedClass(
+                name = name,
+                isInterface = klass.isInterface,
+                isAbstract = klass.modality == Modality.ABSTRACT,
+                superClass = superType,
+                superInterfaces = superInterfaces,
+                typeParameters = typeParameters,
+                members = members,
+                nestedClasses = nestedClasses,
+                ir = klass
             )
         }
-
-        return exportedClass
     }
 
     private fun exportAsEnumMember(
diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/export/ExportModelToJsStatements.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/export/ExportModelToJsStatements.kt
index 04cd437..ce0722f 100644
--- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/export/ExportModelToJsStatements.kt
+++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/export/ExportModelToJsStatements.kt
@@ -84,14 +84,25 @@
             is ExportedConstructor -> emptyList()
             is ExportedConstructSignature -> emptyList()
 
+            is ExportedObject -> {
+                require(namespace != null) { "Only namespaced objects are allowed" }
+                val getter = JsNameRef(namer.getNameForStaticDeclaration(declaration.irGetter))
+
+                val newNameSpace = jsElementAccess(declaration.name, namespace)
+
+                val objectProperties = declaration.members.filterIsInstance<ExportedObject>()
+
+                val staticsExport = (objectProperties + declaration.nestedClasses)
+                    .flatMap { generateDeclarationExport(it, newNameSpace, esModules) }
+
+                listOf(defineProperty(namespace, declaration.name, getter, null).makeStmt()) + staticsExport
+            }
+
             is ExportedProperty -> {
                 require(namespace != null) { "Only namespaced properties are allowed" }
-                val underlying: List<JsStatement> = declaration.exportedObject?.let {
-                    generateDeclarationExport(it, null, esModules)
-                } ?: emptyList()
                 val getter = declaration.irGetter?.let { JsNameRef(namer.getNameForStaticDeclaration(it)) }
                 val setter = declaration.irSetter?.let { JsNameRef(namer.getNameForStaticDeclaration(it)) }
-                listOf(defineProperty(namespace, declaration.name, getter, setter).makeStmt()) + underlying
+                listOf(defineProperty(namespace, declaration.name, getter, setter).makeStmt())
             }
 
             is ErrorDeclaration -> emptyList()
@@ -121,15 +132,13 @@
                     .takeIf { !declaration.ir.isInner }.orEmpty()
 
                 // Nested objects are exported as static properties
-                val staticProperties = declaration.members.mapNotNull {
-                    (it as? ExportedProperty)?.takeIf { it.isStatic }
-                }
+                val objectProperties = declaration.members.filterIsInstance<ExportedObject>()
 
                 val innerClassesAssignments = declaration.nestedClasses
                     .filter { it.ir.isInner }
                     .map { it.generateInnerClassAssignment(declaration) }
 
-                val staticsExport = (staticFunctions + staticProperties + declaration.nestedClasses)
+                val staticsExport = (staticFunctions + objectProperties + declaration.nestedClasses)
                     .flatMap { generateDeclarationExport(it, newNameSpace, esModules) }
 
                 listOfNotNull(klassExport) + staticsExport + innerClassesAssignments
diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/export/ExportModelToTsDeclarations.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/export/ExportModelToTsDeclarations.kt
index 28e3475..51059b2 100644
--- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/export/ExportModelToTsDeclarations.kt
+++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/export/ExportModelToTsDeclarations.kt
@@ -5,314 +5,400 @@
 
 package org.jetbrains.kotlin.ir.backend.js.export
 
+import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
+import org.jetbrains.kotlin.ir.backend.js.utils.getFqNameWithJsNameWhenAvailable
 import org.jetbrains.kotlin.ir.backend.js.utils.getJsNameOrKotlinName
 import org.jetbrains.kotlin.ir.backend.js.utils.sanitizeName
 import org.jetbrains.kotlin.ir.declarations.IrClass
 import org.jetbrains.kotlin.ir.util.parentAsClass
 import org.jetbrains.kotlin.js.common.isValidES5Identifier
+import org.jetbrains.kotlin.name.Name
 import org.jetbrains.kotlin.serialization.js.ModuleKind
 
+const val Nullable = "Nullable"
+const val doNotImplementIt = "__doNotImplementIt"
+const val objectInstances = "__object_instances__"
+const val Instance = "INSTANCE"
 // TODO: Support module kinds other than plain
 
 fun ExportedModule.toTypeScript(): String {
-    return wrapTypeScript(name, moduleKind, declarations.toTypeScript(moduleKind))
+    return ExportModelToTsDeclarations().generateTypeScriptFor(name, moduleKind, this)
 }
 
-fun wrapTypeScript(name: String, moduleKind: ModuleKind, dts: String): String {
-    val declareKeyword = when (moduleKind) {
-        ModuleKind.PLAIN -> ""
-        else -> "declare "
-    }
-    val types = """
-       type Nullable<T> = T | null | undefined
-       ${declareKeyword}const __doNotImplementIt: unique symbol
-       type __doNotImplementIt = typeof __doNotImplementIt
-    """.trimIndent().prependIndent(moduleKind.indent) + "\n"
-
-    val declarationsDts = types + dts
-
-    val namespaceName = sanitizeName(name, withHash = false)
-
-    return when (moduleKind) {
-        ModuleKind.PLAIN -> "declare namespace $namespaceName {\n$declarationsDts\n}\n"
-        ModuleKind.AMD, ModuleKind.COMMON_JS, ModuleKind.ES -> declarationsDts
-        ModuleKind.UMD -> "$declarationsDts\nexport as namespace $namespaceName;"
-    }
-}
-
-private val ModuleKind.indent: String
-    get() = if (this == ModuleKind.PLAIN) "    " else ""
-
 fun List<ExportedDeclaration>.toTypeScript(moduleKind: ModuleKind): String {
-    return joinToString("\n") {
-        it.toTypeScript(
-            indent = moduleKind.indent,
-            prefix = if (moduleKind == ModuleKind.PLAIN) "" else "export "
-        )
-    }
+    return ExportModelToTsDeclarations().generateTypeScriptFor(moduleKind, this)
 }
 
-fun List<ExportedDeclaration>.toTypeScript(indent: String): String =
-    joinToString("") { it.toTypeScript(indent) + "\n" }
+private class ExportModelToTsDeclarations {
+    private var declaredObjectClasses = mutableListOf<ExportedClass>()
 
-fun ExportedDeclaration.toTypeScript(indent: String, prefix: String = ""): String =
-    indent + when (this) {
-        is ErrorDeclaration -> "/* ErrorDeclaration: $message */"
+    fun generateTypeScriptFor(name: String, moduleKind: ModuleKind, module: ExportedModule): String {
+        val declareKeyword = when (moduleKind) {
+            ModuleKind.PLAIN -> ""
+            else -> "declare "
+        }
+        val types = """
+           type $Nullable<T> = T | null | undefined
+           ${declareKeyword}const $doNotImplementIt: unique symbol
+           type $doNotImplementIt = typeof $doNotImplementIt
+        """.trimIndent().prependIndent(moduleKind.indent) + "\n"
 
-        is ExportedNamespace ->
-            "${prefix}namespace $name {\n" + declarations.toTypeScript("$indent    ") + "$indent}"
+        val declarationsDts =
+            types + module.declarations.toTypeScript(moduleKind)
 
-        is ExportedFunction -> {
-            val visibility = if (isProtected) "protected " else ""
+        val namespaceName = sanitizeName(name, withHash = false)
 
-            val keyword: String = when {
-                isMember -> when {
-                    isStatic -> "static "
-                    isAbstract -> "abstract "
-                    else -> ""
+        return when (moduleKind) {
+            ModuleKind.PLAIN -> "declare namespace $namespaceName {\n$declarationsDts\n}\n"
+            ModuleKind.AMD, ModuleKind.COMMON_JS, ModuleKind.ES -> declarationsDts
+            ModuleKind.UMD -> "$declarationsDts\nexport as namespace $namespaceName;"
+        }
+    }
+
+    fun generateTypeScriptFor(moduleKind: ModuleKind, exportedDeclarations: List<ExportedDeclaration>): String {
+       return exportedDeclarations.toTypeScript(moduleKind)
+    }
+
+    private val ModuleKind.indent: String
+        get() = if (this == ModuleKind.PLAIN) "    " else ""
+
+    private fun List<ExportedDeclaration>.toTypeScript(moduleKind: ModuleKind): String {
+        return joinToString("\n") {
+            it.toTypeScript(
+                indent = moduleKind.indent,
+                prefix = if (moduleKind == ModuleKind.PLAIN) "" else "export "
+            )
+        }
+    }
+
+    private fun List<ExportedDeclaration>.toTypeScript(indent: String): String =
+        joinToString("") { it.toTypeScript(indent) + "\n" }
+
+    private fun ExportedDeclaration.toTypeScript(indent: String, prefix: String = ""): String =
+        indent + when (this) {
+            is ErrorDeclaration -> "/* ErrorDeclaration: $message */"
+
+            is ExportedNamespace -> {
+                val body = declarations.toTypeScript("$indent    ")
+                val objectInstances = if (isSynthetic) "" else "${generateObjectInstancesNamespace("$indent    ")}\n"
+                "${prefix}namespace $name {\n" + body + objectInstances + "$indent}"
+            }
+
+            is ExportedFunction -> {
+                val visibility = if (isProtected) "protected " else ""
+
+                val keyword: String = when {
+                    isMember -> when {
+                        isStatic -> "static "
+                        isAbstract -> "abstract "
+                        else -> ""
+                    }
+                    else -> "function "
                 }
-                else -> "function "
-            }
 
-            val renderedParameters = parameters.joinToString(", ") { it.toTypeScript(indent) }
+                val renderedParameters = parameters.joinToString(", ") { it.toTypeScript(indent) }
 
-            val renderedTypeParameters =
-                if (typeParameters.isNotEmpty())
-                    "<" + typeParameters.joinToString(", ") { it.toTypeScript(indent) } + ">"
-                else
-                    ""
+                val renderedTypeParameters =
+                    if (typeParameters.isNotEmpty())
+                        "<" + typeParameters.joinToString(", ") { it.toTypeScript(indent) } + ">"
+                    else
+                        ""
 
-            val renderedReturnType = returnType.toTypeScript(indent)
-            val containsUnresolvedChar = !name.isValidES5Identifier()
+                val renderedReturnType = returnType.toTypeScript(indent)
+                val containsUnresolvedChar = !name.isValidES5Identifier()
 
-            val escapedName = when {
-                isMember && containsUnresolvedChar -> "\"$name\""
-                else -> name
-            }
-
-            if (!isMember && containsUnresolvedChar) "" else "${prefix}$visibility$keyword$escapedName$renderedTypeParameters($renderedParameters): $renderedReturnType;"
-        }
-
-        is ExportedConstructor -> {
-            val renderedParameters = parameters.joinToString(", ") { it.toTypeScript(indent) }
-            "${visibility.keyword}constructor($renderedParameters);"
-        }
-
-        is ExportedConstructSignature -> {
-            val renderedParameters = parameters.joinToString(", ") { it.toTypeScript(indent) }
-            "new($renderedParameters): ${returnType.toTypeScript(indent)};"
-        }
-
-        is ExportedProperty -> {
-            val visibility = if (isProtected) "protected " else ""
-            val keyword = when {
-                isMember -> (if (isAbstract) "abstract " else "")
-                else -> if (mutable) "let " else "const "
-            }
-            val possibleStatic = if (isMember && isStatic) "static " else ""
-            val containsUnresolvedChar = !name.isValidES5Identifier()
-            val memberName = when {
-                isMember && containsUnresolvedChar -> "\"$name\""
-                else -> name
-            }
-            val typeToTypeScript = type.toTypeScript(indent)
-            if (isMember && !isField) {
-                val getter = "$prefix$visibility$possibleStatic${keyword}get $memberName(): $typeToTypeScript;"
-                if (!mutable) getter
-                else getter + "\n" + "$indent$prefix$visibility$possibleStatic${keyword}set $memberName(value: $typeToTypeScript);"
-            } else {
-                if (!isMember && containsUnresolvedChar) ""
-                else {
-                    val readonly = if (isMember && !mutable) "readonly " else ""
-                    "$prefix$visibility$possibleStatic$keyword$readonly$memberName: $typeToTypeScript;"
+                val escapedName = when {
+                    isMember && containsUnresolvedChar -> "\"$name\""
+                    else -> name
                 }
+
+                if (!isMember && containsUnresolvedChar) "" else "${prefix}$visibility$keyword$escapedName$renderedTypeParameters($renderedParameters): $renderedReturnType;"
             }
-        }
 
-        is ExportedClass -> {
-            val keyword = if (isInterface) "interface" else "class"
-            val superInterfacesKeyword = if (isInterface) "extends" else "implements"
+            is ExportedConstructor -> {
+                val renderedParameters = parameters.joinToString(", ") { it.toTypeScript(indent) }
+                "${visibility.keyword}constructor($renderedParameters);"
+            }
 
-            val superClassClause = superClass?.let { it.toExtendsClause(indent) } ?: ""
-            val superInterfacesClause = superInterfaces.toImplementsClause(superInterfacesKeyword, indent)
+            is ExportedConstructSignature -> {
+                val renderedParameters = parameters.joinToString(", ") { it.toTypeScript(indent) }
+                "new($renderedParameters): ${returnType.toTypeScript(indent)};"
+            }
 
-            val members = members
-                .let { if (shouldNotBeImplemented()) it.withMagicProperty() else it }
-                .map {
-                    if (!ir.isInner || it !is ExportedFunction || !it.isStatic) {
-                        it
-                    } else {
-                        // Remove $outer argument from secondary constructors of inner classes
-                        it.copy(parameters = it.parameters.drop(1))
+            is ExportedProperty -> {
+                val visibility = if (isProtected) "protected " else ""
+                val keyword = when {
+                    isMember -> (if (isAbstract) "abstract " else "")
+                    else -> if (mutable) "let " else "const "
+                }
+                val possibleStatic = if (isMember && isStatic) "static " else ""
+                val containsUnresolvedChar = !name.isValidES5Identifier()
+                val memberName = when {
+                    isMember && containsUnresolvedChar -> "\"$name\""
+                    else -> name
+                }
+                val typeToTypeScript = type.toTypeScript(indent)
+                if (isMember && !isField) {
+                    val getter = "$prefix$visibility$possibleStatic${keyword}get $memberName(): $typeToTypeScript;"
+                    if (!mutable) getter
+                    else getter + "\n" + "$indent$prefix$visibility$possibleStatic${keyword}set $memberName(value: $typeToTypeScript);"
+                } else {
+                    if (!isMember && containsUnresolvedChar) ""
+                    else {
+                        val readonly = if (isMember && !mutable) "readonly " else ""
+                        "$prefix$visibility$possibleStatic$keyword$readonly$memberName: $typeToTypeScript;"
                     }
                 }
+            }
 
-            val (innerClasses, nonInnerClasses) = nestedClasses.partition { it.ir.isInner }
-            val innerClassesProperties = innerClasses.map { it.toReadonlyProperty() }
-            val membersString = (members + innerClassesProperties).joinToString("") { it.toTypeScript("$indent    ") + "\n" }
+            is ExportedObject ->
+                generateObjectProperty().toTypeScript("", prefix).also {
+                    declaredObjectClasses.add(generateObjectClass())
+                }
 
-            // If there are no exported constructors, add a private constructor to disable default one
-            val privateCtorString =
-                if (!isInterface && !isAbstract && members.none { it is ExportedConstructor })
-                    "$indent    private constructor();\n"
-                else
-                    ""
+            is ExportedClass -> {
+                val keyword = if (isInterface) "interface" else "class"
+                val superInterfacesKeyword = if (isInterface) "extends" else "implements"
 
-            val renderedTypeParameters =
-                if (typeParameters.isNotEmpty())
-                    "<" + typeParameters.joinToString(", ") + ">"
-                else
-                    ""
+                val superClassClause = superClass?.let { it.toExtendsClause(indent) } ?: ""
+                val superInterfacesClause = superInterfaces.toImplementsClause(superInterfacesKeyword, indent)
 
-            val modifiers = if (isAbstract && !isInterface) "abstract " else ""
+                val members = members
+                    .let { if (shouldNotBeImplemented()) it.withMagicProperty() else it }
+                    .map {
+                        if (!ir.isInner || it !is ExportedFunction || !it.isStatic) {
+                            it
+                        } else {
+                            // Remove $outer argument from secondary constructors of inner classes
+                            it.copy(parameters = it.parameters.drop(1))
+                        }
+                    }
 
-            val bodyString = privateCtorString + membersString + indent
+                val (innerClasses, nonInnerClasses) = nestedClasses.partition { it.ir.isInner }
+                val innerClassesProperties = innerClasses.map { it.toReadonlyProperty() }
+                val membersString = (members + innerClassesProperties).joinToString("") { it.toTypeScript("$indent    ") + "\n" }
 
-            val nestedClasses = nonInnerClasses + innerClasses.map { it.withProtectedConstructors() }
-            val klassExport =
-                "$prefix$modifiers$keyword $name$renderedTypeParameters$superClassClause$superInterfacesClause {\n$bodyString}"
-            val staticsExport =
-                if (nestedClasses.isNotEmpty()) "\n" + ExportedNamespace(name, nestedClasses).toTypeScript(indent, prefix) else ""
+                // If there are no exported constructors, add a private constructor to disable default one
+                val privateCtorString =
+                    if (!isInterface && !isAbstract && members.none { it is ExportedConstructor })
+                        "$indent    private constructor();\n"
+                    else
+                        ""
 
-            if (name.isValidES5Identifier()) klassExport + staticsExport else ""
+                val renderedTypeParameters =
+                    if (typeParameters.isNotEmpty())
+                        "<" + typeParameters.joinToString(", ") + ">"
+                    else
+                        ""
+
+                val modifiers = if (isAbstract && !isInterface) "abstract " else ""
+
+                val bodyString = privateCtorString + membersString + indent
+
+                val nestedClasses = nonInnerClasses + innerClasses.map { it.withProtectedConstructors() }
+                val klassExport =
+                    "$prefix$modifiers$keyword $name$renderedTypeParameters$superClassClause$superInterfacesClause {\n$bodyString}"
+                val staticsExport =
+                    if (nestedClasses.isNotEmpty()) "\n" + ExportedNamespace(name, nestedClasses, true).toTypeScript(indent, prefix) else ""
+
+                if (name.isValidES5Identifier()) klassExport + staticsExport else ""
+            }
+        }
+
+    private fun ExportedObject.generateObjectClass(): ExportedClass {
+        return ExportedClass(
+            ir = ir,
+            name = Instance,
+            isInterface = false,
+            isAbstract = false,
+            members = members,
+            superClass = superClass,
+            superInterfaces = superInterfaces,
+            typeParameters = emptyList(),
+            nestedClasses = nestedClasses,
+        )
+    }
+
+    private fun ExportedObject.generateObjectProperty(): ExportedProperty {
+        val instancesNamespace = Name.identifier(objectInstances)
+        val instanceRef = Name.identifier(Instance)
+        val classRef = ir.getFqNameWithJsNameWhenAvailable(instancesNamespace).child(instanceRef).asString()
+        val type = ExportedType.IntersectionType(
+            ExportedType.ClassType(classRef, emptyList(), ir),
+            ExportedType.TypeOf(classRef)
+        )
+        return ExportedProperty(
+            name = name,
+            type = type,
+            mutable = false,
+            isMember = ir.parent is IrClass,
+            isStatic = !ir.isInner,
+            isAbstract = false,
+            isProtected = ir.visibility == DescriptorVisibilities.PROTECTED,
+            irGetter = irGetter,
+            irSetter = null,
+            isField = false,
+        )
+    }
+
+    private fun ExportedType.toExtendsClause(indent: String): String {
+        val isImplicitlyExportedType = this is ExportedType.ImplicitlyExportedType
+        val extendsClause = " extends ${toTypeScript(indent, isImplicitlyExportedType)}"
+        return when {
+            isImplicitlyExportedType -> " /*$extendsClause */"
+            else -> extendsClause
         }
     }
 
-fun ExportedType.toExtendsClause(indent: String): String {
-    val isImplicitlyExportedType = this is ExportedType.ImplicitlyExportedType
-    val extendsClause = " extends ${toTypeScript(indent, isImplicitlyExportedType)}"
-    return when {
-        isImplicitlyExportedType -> " /*$extendsClause */"
-        else -> extendsClause
-    }
-}
-
-fun List<ExportedType>.toImplementsClause(superInterfacesKeyword: String, indent: String): String {
-    val (exportedInterfaces, nonExportedInterfaces) = partition { it !is ExportedType.ImplicitlyExportedType }
-    val listOfNonExportedInterfaces = nonExportedInterfaces.joinToString(", ") {
-        (it as ExportedType.ImplicitlyExportedType).type.toTypeScript(indent, true)
-    }
-    return when {
-        exportedInterfaces.isEmpty() && nonExportedInterfaces.isNotEmpty() ->
-            " /* $superInterfacesKeyword $listOfNonExportedInterfaces */"
-        exportedInterfaces.isNotEmpty() -> {
-            val nonExportedInterfacesTsString = if (nonExportedInterfaces.isNotEmpty()) "/*, $listOfNonExportedInterfaces */" else ""
-            " $superInterfacesKeyword " + exportedInterfaces.joinToString(", ") { it.toTypeScript(indent) } + nonExportedInterfacesTsString
+    private fun List<ExportedType>.toImplementsClause(superInterfacesKeyword: String, indent: String): String {
+        val (exportedInterfaces, nonExportedInterfaces) = partition { it !is ExportedType.ImplicitlyExportedType }
+        val listOfNonExportedInterfaces = nonExportedInterfaces.joinToString(", ") {
+            (it as ExportedType.ImplicitlyExportedType).type.toTypeScript(indent, true)
         }
-        else -> ""
+        return when {
+            exportedInterfaces.isEmpty() && nonExportedInterfaces.isNotEmpty() ->
+                " /* $superInterfacesKeyword $listOfNonExportedInterfaces */"
+            exportedInterfaces.isNotEmpty() -> {
+                val nonExportedInterfacesTsString = if (nonExportedInterfaces.isNotEmpty()) "/*, $listOfNonExportedInterfaces */" else ""
+                " $superInterfacesKeyword " + exportedInterfaces.joinToString(", ") { it.toTypeScript(indent) } + nonExportedInterfacesTsString
+            }
+            else -> ""
+        }
     }
-}
 
-fun ExportedClass.shouldNotBeImplemented(): Boolean {
-    return (isInterface && !ir.isExternal) || superInterfaces.any { it is ExportedType.ClassType && !it.ir.isExternal }
-}
+    private fun ExportedClass.shouldNotBeImplemented(): Boolean {
+        return (isInterface && !ir.isExternal) || superInterfaces.any { it is ExportedType.ClassType && !it.ir.isExternal }
+    }
 
-fun List<ExportedDeclaration>.withMagicProperty(): List<ExportedDeclaration> {
-    return plus(
-        ExportedProperty(
-            "__doNotUseIt",
-            ExportedType.TypeParameter("__doNotImplementIt"),
+    private fun List<ExportedDeclaration>.withMagicProperty(): List<ExportedDeclaration> {
+        return plus(
+            ExportedProperty(
+                "__doNotUseIt",
+                ExportedType.TypeParameter(doNotImplementIt),
+                mutable = false,
+                isMember = true,
+                isStatic = false,
+                isAbstract = false,
+                isProtected = false,
+                isField = true,
+                irGetter = null,
+                irSetter = null,
+            )
+        )
+    }
+
+    private fun IrClass.asNestedClassAccess(): String {
+        val name = getJsNameOrKotlinName().identifier
+        if (parent !is IrClass) return name
+        return "${parentAsClass.asNestedClassAccess()}.$name"
+    }
+
+    private fun ExportedClass.withProtectedConstructors(): ExportedClass {
+        return copy(members = members.map {
+            if (it !is ExportedConstructor || it.isProtected) {
+                it
+            } else {
+                it.copy(visibility = ExportedVisibility.PROTECTED)
+            }
+        })
+    }
+
+    private fun ExportedClass.toReadonlyProperty(): ExportedProperty {
+        val innerClassReference = ir.asNestedClassAccess()
+        val allPublicConstructors = members.asSequence()
+            .filterIsInstance<ExportedConstructor>()
+            .filterNot { it.isProtected }
+            .map {
+                ExportedConstructSignature(
+                    parameters = it.parameters.drop(1),
+                    returnType = ExportedType.TypeParameter(innerClassReference),
+                )
+            }
+            .toList()
+
+        val type = ExportedType.IntersectionType(
+            ExportedType.InlineInterfaceType(allPublicConstructors),
+            ExportedType.TypeOf(innerClassReference)
+        )
+
+        return ExportedProperty(
+            name = name,
+            type = type,
             mutable = false,
             isMember = true,
             isStatic = false,
             isAbstract = false,
             isProtected = false,
-            isField = true,
+            isField = false,
             irGetter = null,
-            irSetter = null,
+            irSetter = null
         )
-    )
-}
+    }
 
-fun IrClass.asNestedClassAccess(): String {
-    val name = getJsNameOrKotlinName().identifier
-    if (parent !is IrClass) return name
-    return "${parentAsClass.asNestedClassAccess()}.$name"
-}
+    private fun ExportedParameter.toTypeScript(indent: String): String {
+        val name = sanitizeName(name, withHash = false)
+        val type = type.toTypeScript(indent)
+        val questionMark = if (hasDefaultValue) "?" else ""
+        return "$name$questionMark: $type"
+    }
 
-fun ExportedClass.withProtectedConstructors(): ExportedClass {
-    return copy(members = members.map {
-        if (it !is ExportedConstructor || it.isProtected) {
-            it
+    private fun ExportedType.toTypeScript(indent: String, isInCommentContext: Boolean = false): String = when (this) {
+        is ExportedType.Primitive -> typescript
+        is ExportedType.Array -> "Array<${elementType.toTypeScript(indent, isInCommentContext)}>"
+        is ExportedType.Function -> "(" + parameterTypes
+            .withIndex()
+            .joinToString(", ") { (index, type) ->
+                "p$index: ${type.toTypeScript(indent, isInCommentContext)}"
+            } + ") => " + returnType.toTypeScript(indent, isInCommentContext)
+
+        is ExportedType.ClassType ->
+            name + if (arguments.isNotEmpty()) "<${arguments.joinToString(", ") { it.toTypeScript(indent, isInCommentContext) }}>" else ""
+        is ExportedType.TypeOf ->
+            "typeof $name"
+
+        is ExportedType.ErrorType -> if (isInCommentContext) comment else "any /*$comment*/"
+        is ExportedType.Nullable -> "$Nullable<" + baseType.toTypeScript(indent, isInCommentContext) + ">"
+        is ExportedType.InlineInterfaceType -> {
+            members.joinToString(prefix = "{\n", postfix = "$indent}", separator = "") { it.toTypeScript("$indent    ") + "\n" }
+        }
+        is ExportedType.IntersectionType -> {
+            lhs.toTypeScript(indent) + " & " + rhs.toTypeScript(indent, isInCommentContext)
+        }
+        is ExportedType.UnionType -> {
+            lhs.toTypeScript(indent) + " | " + rhs.toTypeScript(indent, isInCommentContext)
+        }
+        is ExportedType.LiteralType.StringLiteralType -> "\"$value\""
+        is ExportedType.LiteralType.NumberLiteralType -> value.toString()
+        is ExportedType.ImplicitlyExportedType -> {
+            val typeString = type.toTypeScript("", true)
+            if (isInCommentContext) typeString else ExportedType.Primitive.Any.toTypeScript(indent) + "/* $typeString */"
+        }
+        is ExportedType.TypeParameter -> if (constraint == null) {
+            name
         } else {
-            it.copy(visibility = ExportedVisibility.PROTECTED)
+            "$name extends ${constraint.toTypeScript(indent, isInCommentContext)}"
         }
-    })
-}
+    }
 
-fun ExportedClass.toReadonlyProperty(): ExportedProperty {
-    val innerClassReference = ir.asNestedClassAccess()
-    val allPublicConstructors = members.asSequence()
-        .filterIsInstance<ExportedConstructor>()
-        .filterNot { it.isProtected }
-        .map {
-            ExportedConstructSignature(
-                parameters = it.parameters.drop(1),
-                returnType = ExportedType.TypeParameter(innerClassReference),
-            )
+    private fun generateObjectInstancesNamespace(indent: String): String {
+        if (declaredObjectClasses.isEmpty()) return ""
+
+        val result = StringBuilder()
+        var previousDeclaredObjectClasses = declaredObjectClasses
+        declaredObjectClasses = mutableListOf()
+
+        while (previousDeclaredObjectClasses.isNotEmpty()) {
+            result.append(previousDeclaredObjectClasses.toExportedNamespace().toTypeScript("$indent    "))
+            previousDeclaredObjectClasses = declaredObjectClasses
+            declaredObjectClasses = mutableListOf()
         }
-        .toList()
 
-    val type = ExportedType.IntersectionType(
-        ExportedType.InlineInterfaceType(allPublicConstructors),
-        ExportedType.TypeOf(innerClassReference)
-    )
+        return "${indent}namespace $objectInstances {\n$result\n$indent}"
+    }
 
-    return ExportedProperty(
-        name = name,
-        type = type,
-        mutable = false,
-        isMember = true,
-        isStatic = false,
-        isAbstract = false,
-        isProtected = false,
-        isField = false,
-        irGetter = null,
-        irSetter = null
-    )
+    private fun List<ExportedClass>.toExportedNamespace(): List<ExportedNamespace> {
+        return map {
+            ExportedNamespace(it.ir.getFqNameWithJsNameWhenAvailable(false).asString(), listOf(it), true)
+        }
+    }
 }
-
-fun ExportedParameter.toTypeScript(indent: String): String {
-    val name = sanitizeName(name, withHash = false)
-    val type = type.toTypeScript(indent)
-    val questionMark = if (hasDefaultValue) "?" else ""
-    return "$name$questionMark: $type"
-}
-
-fun ExportedType.toTypeScript(indent: String, isInCommentContext: Boolean = false): String = when (this) {
-    is ExportedType.Primitive -> typescript
-    is ExportedType.Array -> "Array<${elementType.toTypeScript(indent, isInCommentContext)}>"
-    is ExportedType.Function -> "(" + parameterTypes
-        .withIndex()
-        .joinToString(", ") { (index, type) ->
-            "p$index: ${type.toTypeScript(indent, isInCommentContext)}"
-        } + ") => " + returnType.toTypeScript(indent, isInCommentContext)
-
-    is ExportedType.ClassType ->
-        name + if (arguments.isNotEmpty()) "<${arguments.joinToString(", ") { it.toTypeScript(indent, isInCommentContext) }}>" else ""
-    is ExportedType.TypeOf ->
-        "typeof $name"
-
-    is ExportedType.ErrorType -> if (isInCommentContext) comment else "any /*$comment*/"
-    is ExportedType.Nullable -> "Nullable<" + baseType.toTypeScript(indent, isInCommentContext) + ">"
-    is ExportedType.InlineInterfaceType -> {
-        members.joinToString(prefix = "{\n", postfix = "$indent}", separator = "") { it.toTypeScript("$indent    ") + "\n" }
-    }
-    is ExportedType.IntersectionType -> {
-        lhs.toTypeScript(indent) + " & " + rhs.toTypeScript(indent, isInCommentContext)
-    }
-    is ExportedType.UnionType -> {
-        lhs.toTypeScript(indent) + " | " + rhs.toTypeScript(indent, isInCommentContext)
-    }
-    is ExportedType.LiteralType.StringLiteralType -> "\"$value\""
-    is ExportedType.LiteralType.NumberLiteralType -> value.toString()
-    is ExportedType.ImplicitlyExportedType -> {
-        val typeString = type.toTypeScript("", true)
-        if (isInCommentContext) typeString else ExportedType.Primitive.Any.toTypeScript(indent) + "/* $typeString */"
-    }
-    is ExportedType.TypeParameter -> if (constraint == null) {
-        name
-    } else {
-        "$name extends ${constraint.toTypeScript(indent, isInCommentContext)}"
-    }
-}
\ No newline at end of file
diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrModuleToJsTransformerTmp.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrModuleToJsTransformerTmp.kt
index 7e454cf..d43f35c 100644
--- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrModuleToJsTransformerTmp.kt
+++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrModuleToJsTransformerTmp.kt
@@ -84,7 +84,7 @@
             }
         }
 
-        val dts = wrapTypeScript(mainModuleName, moduleKind, exportData.values.flatMap { it.values.flatMap { it } }.toTypeScript(moduleKind))
+        val dts = ExportedModule(mainModuleName, moduleKind, exportData.values.flatMap { it.values.flatten() }).toTypeScript()
 
         modules.forEach { module ->
             module.files.forEach { StaticMembersLowering(backendContext).lower(it) }
diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/IrJsUtils.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/IrJsUtils.kt
index 1c8b7ed..1df0f73 100644
--- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/IrJsUtils.kt
+++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/IrJsUtils.kt
@@ -7,6 +7,7 @@
 
 import org.jetbrains.kotlin.descriptors.isClass
 import org.jetbrains.kotlin.descriptors.isInterface
+import org.jetbrains.kotlin.ir.IrElement
 import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
 import org.jetbrains.kotlin.ir.backend.js.export.isExported
 import org.jetbrains.kotlin.ir.declarations.*
@@ -14,6 +15,7 @@
 import org.jetbrains.kotlin.ir.symbols.IrReturnableBlockSymbol
 import org.jetbrains.kotlin.ir.util.parentClassOrNull
 import org.jetbrains.kotlin.name.FqName
+import org.jetbrains.kotlin.name.Name
 
 fun IrDeclaration.isExportedMember(context: JsIrBackendContext) =
     (this is IrDeclarationWithVisibility && visibility.isPublicAPI) &&
@@ -29,15 +31,25 @@
     return target.owner.statements.lastOrNull() === this
 }
 
-fun IrDeclarationWithName.getFqNameWithJsNameWhenAvailable(shouldIncludePackage: Boolean): FqName {
-    val name = getJsNameOrKotlinName()
-    return when (val parent = parent) {
-        is IrDeclarationWithName -> parent.getFqNameWithJsNameWhenAvailable(shouldIncludePackage).child(name)
-        is IrPackageFragment -> getKotlinOrJsQualifier(parent, shouldIncludePackage)?.child(name) ?: FqName(name.identifier)
-        else -> FqName(name.identifier)
+private fun IrElement.getFqNameWithJsNameWhenAvailable(getSubQualifier: (IrPackageFragment) -> FqName?): FqName? {
+    return when (this) {
+        is IrDeclarationWithName -> {
+            val name = getJsNameOrKotlinName()
+            return parent.getFqNameWithJsNameWhenAvailable(getSubQualifier)?.child(name) ?: FqName(name.identifier)
+        }
+        is IrPackageFragment -> getSubQualifier(this)
+        else -> null
     }
 }
 
+fun IrDeclarationWithName.getFqNameWithJsNameWhenAvailable(shouldIncludePackage: Boolean): FqName {
+    return getFqNameWithJsNameWhenAvailable { getKotlinOrJsQualifier(it, shouldIncludePackage) }!!
+}
+
+fun IrDeclarationWithName.getFqNameWithJsNameWhenAvailable(subQualifier: Name): FqName {
+    return getFqNameWithJsNameWhenAvailable { getKotlinOrJsQualifier(it, true)?.child(subQualifier) }!!
+}
+
 private fun getKotlinOrJsQualifier(parent: IrPackageFragment, shouldIncludePackage: Boolean): FqName? {
     return (parent as? IrFile)?.getJsQualifier()?.let { FqName(it) } ?: parent.fqName.takeIf { shouldIncludePackage }
 }
diff --git a/js/js.translator/testData/typescript-export/declarations/declarations.kt b/js/js.translator/testData/typescript-export/declarations/declarations.kt
index e303e35..703ba96 100644
--- a/js/js.translator/testData/typescript-export/declarations/declarations.kt
+++ b/js/js.translator/testData/typescript-export/declarations/declarations.kt
@@ -246,4 +246,43 @@
 
     @JsName("NestedJsName")
     class __NestJsNameTest(@JsName("value") val __value: Int)
+}
+
+//@JsExport
+//object Parent {
+//    val value = 4
+//    fun test(): String = "Test"
+//
+//    class NestedClass
+//    interface NestedInterface
+//    object NestedObject
+//    enum class NestedEnumClass { A, B }
+//
+//    class NestedParentClass {
+//        class NestedChildClass {
+//            companion object {
+//                class CompanionNestedClass
+//            }
+//        }
+//    }
+//
+//    object NestedParentObject {
+//        object NestedChildObject
+//    }
+//}
+
+@JsExport
+object Parent {
+    object Nested1 {
+       class Nested2 {
+           companion object {
+               class Nested3
+           }
+       }
+    }
+}
+
+@JsExport
+fun createNested3(): Parent.Nested1.Nested2.Companion.Nested3 {
+    return Parent.Nested1.Nested2.Companion.Nested3()
 }
\ No newline at end of file