feat(@JsExport for interfaces): add ability to export interfaces in TypeScript.
This commit is contained in:
@@ -106,7 +106,7 @@ sealed class ExportedType {
|
||||
val returnType: ExportedType
|
||||
) : ExportedType()
|
||||
|
||||
class ClassType(val name: String, val arguments: List<ExportedType>) : ExportedType()
|
||||
class ClassType(val name: String, val arguments: List<ExportedType>, val ir: IrClass) : ExportedType()
|
||||
class TypeParameter(val name: String) : ExportedType()
|
||||
class Nullable(val baseType: ExportedType) : ExportedType()
|
||||
class ErrorType(val comment: String) : ExportedType()
|
||||
|
||||
+2
-1
@@ -483,7 +483,8 @@ class ExportModelGenerator(
|
||||
ClassKind.ENUM_CLASS,
|
||||
ClassKind.INTERFACE -> ExportedType.ClassType(
|
||||
name,
|
||||
type.arguments.map { exportTypeArgument(it) }
|
||||
type.arguments.map { exportTypeArgument(it) },
|
||||
klass
|
||||
)
|
||||
}.withImplicitlyExported(isImplicitlyExported)
|
||||
}
|
||||
|
||||
+38
-8
@@ -19,7 +19,15 @@ fun ExportedModule.toTypeScript(): String {
|
||||
}
|
||||
|
||||
fun wrapTypeScript(name: String, moduleKind: ModuleKind, dts: String): String {
|
||||
val types = "${moduleKind.indent}type Nullable<T> = T | null | undefined\n"
|
||||
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
|
||||
|
||||
@@ -117,14 +125,16 @@ fun ExportedDeclaration.toTypeScript(indent: String, prefix: String = ""): Strin
|
||||
val superClassClause = superClass?.let { it.toExtendsClause(indent) } ?: ""
|
||||
val superInterfacesClause = superInterfaces.toImplementsClause(superInterfacesKeyword, indent)
|
||||
|
||||
val members = members.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 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 (innerClasses, nonInnerClasses) = nestedClasses.partition { it.ir.isInner }
|
||||
val innerClassesProperties = innerClasses.map { it.toReadonlyProperty() }
|
||||
@@ -179,6 +189,26 @@ fun List<ExportedType>.toImplementsClause(superInterfacesKeyword: String, indent
|
||||
}
|
||||
}
|
||||
|
||||
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"),
|
||||
mutable = false,
|
||||
isMember = true,
|
||||
isStatic = false,
|
||||
isAbstract = false,
|
||||
isProtected = false,
|
||||
irGetter = null,
|
||||
irSetter = null
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
fun IrClass.asNestedClassAccess(): String {
|
||||
val name = getJsNameOrKotlinName().identifier
|
||||
if (parent !is IrClass) return name
|
||||
|
||||
+51
-31
@@ -6,6 +6,7 @@
|
||||
package org.jetbrains.kotlin.ir.backend.js.transformers.irToJs
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
|
||||
import org.jetbrains.kotlin.descriptors.DescriptorVisibility
|
||||
import org.jetbrains.kotlin.descriptors.Modality
|
||||
import org.jetbrains.kotlin.ir.backend.js.export.isEnumFakeOverriddenDeclaration
|
||||
import org.jetbrains.kotlin.ir.backend.js.export.isExported
|
||||
@@ -125,24 +126,21 @@ class JsClassGenerator(private val irClass: IrClass, val context: JsGenerationCo
|
||||
)
|
||||
}
|
||||
|
||||
fun IrSimpleFunction.accessorRef(): JsNameRef? =
|
||||
when (visibility) {
|
||||
DescriptorVisibilities.PRIVATE -> null
|
||||
else -> JsNameRef(
|
||||
context.getNameForMemberFunction(this),
|
||||
classPrototypeRef
|
||||
)
|
||||
}
|
||||
val overriddenSymbols = property.getter?.overriddenSymbols.orEmpty()
|
||||
|
||||
// Don't generate `defineProperty` if the property overrides a property from an exported class,
|
||||
// because we've already generated `defineProperty` for the base class property.
|
||||
// In other words, we only want to generate `defineProperty` once for each property.
|
||||
// The exception is case when we override val with var,
|
||||
// so we need regenerate `defineProperty` with setter.
|
||||
val noOverriddenGetter = property.getter?.overriddenSymbols?.isEmpty() == true
|
||||
// P.S. If the overridden property is owned by an interface - we should generate defineProperty
|
||||
// for overridden property in the first class which override those properties
|
||||
val hasOverriddenExportedInterfaceProperties = overriddenSymbols.any { it.owner.parentClassOrNull.isExportedInterface() }
|
||||
&& !overriddenSymbols.any { it.owner.parentClassOrNull.isExportedClass() }
|
||||
|
||||
val overriddenExportedGetter = (property.getter?.overriddenSymbols?.isNotEmpty() == true &&
|
||||
property.getter?.isOverriddenExported(context.staticContext.backendContext) == true)
|
||||
val getterOverridesExternal = property.getter?.overridesExternal() == true
|
||||
val overriddenExportedGetter = !property.getter?.overriddenSymbols.isNullOrEmpty() &&
|
||||
property.getter?.isOverriddenExported(context.staticContext.backendContext) == true
|
||||
|
||||
val noOverriddenExportedSetter = property.setter?.isOverriddenExported(context.staticContext.backendContext) == false
|
||||
|
||||
@@ -150,8 +148,9 @@ class JsClassGenerator(private val irClass: IrClass, val context: JsGenerationCo
|
||||
property.isEnumFakeOverriddenDeclaration(context.staticContext.backendContext)
|
||||
|
||||
if (irClass.isExported(context.staticContext.backendContext) &&
|
||||
(noOverriddenGetter || needsOverride) ||
|
||||
property.getter?.overridesExternal() == true ||
|
||||
(overriddenSymbols.isEmpty() || needsOverride) ||
|
||||
hasOverriddenExportedInterfaceProperties ||
|
||||
getterOverridesExternal ||
|
||||
property.getJsName() != null
|
||||
) {
|
||||
|
||||
@@ -168,31 +167,24 @@ class JsClassGenerator(private val irClass: IrClass, val context: JsGenerationCo
|
||||
// set: Foo.prototype._set_prop__a4enbm_k$
|
||||
// });
|
||||
|
||||
val getterForwarder = if (property.getter?.modality == Modality.FINAL) property.getter?.accessorRef()
|
||||
else {
|
||||
property.getter?.propertyAccessorForwarder("getter forwarder") { getterRef ->
|
||||
JsReturn(
|
||||
JsInvocation(
|
||||
getterRef
|
||||
)
|
||||
)
|
||||
val getterForwarder = property.getter
|
||||
.takeIf { it.shouldExportAccessor() }
|
||||
.getOrGenerateIfFinal {
|
||||
propertyAccessorForwarder("getter forwarder") {
|
||||
JsReturn(JsInvocation(it))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val setterForwarder = if (property.setter?.modality == Modality.FINAL) property.setter?.accessorRef()
|
||||
else {
|
||||
property.setter?.let {
|
||||
val setterForwarder = property.setter
|
||||
.takeIf { it.shouldExportAccessor() }
|
||||
.getOrGenerateIfFinal {
|
||||
val setterArgName = JsName("value", false)
|
||||
it.propertyAccessorForwarder("setter forwarder") { setterRef ->
|
||||
JsInvocation(
|
||||
setterRef,
|
||||
JsNameRef(setterArgName)
|
||||
).makeStmt()
|
||||
propertyAccessorForwarder("setter forwarder") {
|
||||
JsInvocation(it, JsNameRef(setterArgName)).makeStmt()
|
||||
}?.apply {
|
||||
parameters.add(JsParameter(setterArgName))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
classBlock.statements += JsExpressionStatement(
|
||||
defineProperty(
|
||||
@@ -209,6 +201,34 @@ class JsClassGenerator(private val irClass: IrClass, val context: JsGenerationCo
|
||||
return classBlock
|
||||
}
|
||||
|
||||
private inline fun IrSimpleFunction?.getOrGenerateIfFinal(generateFunc: IrSimpleFunction.() -> JsFunction?): JsExpression? {
|
||||
if (this == null) return null
|
||||
return if (modality == Modality.FINAL) accessorRef() else generateFunc()
|
||||
}
|
||||
|
||||
private fun IrSimpleFunction?.shouldExportAccessor(): Boolean {
|
||||
if (this == null) return false
|
||||
|
||||
if (parentAsClass.isExported(context.staticContext.backendContext)) return true
|
||||
|
||||
val property = correspondingPropertySymbol!!.owner
|
||||
|
||||
if (property.isOverriddenExported(context.staticContext.backendContext)) {
|
||||
return isOverriddenExported(context.staticContext.backendContext)
|
||||
}
|
||||
|
||||
return overridesExternal() || property.getJsName() != null
|
||||
}
|
||||
|
||||
private fun IrSimpleFunction.accessorRef(): JsNameRef? =
|
||||
when (visibility) {
|
||||
DescriptorVisibilities.PRIVATE -> null
|
||||
else -> JsNameRef(
|
||||
context.getNameForMemberFunction(this),
|
||||
classPrototypeRef
|
||||
)
|
||||
}
|
||||
|
||||
private fun IrSimpleFunction.generateAssignmentIfMangled(memberRef: JsExpression) {
|
||||
if (
|
||||
irClass.isExported(context.staticContext.backendContext) &&
|
||||
|
||||
+6
-3
@@ -143,11 +143,14 @@ fun translateCall(
|
||||
val jsExtensionReceiver = expression.extensionReceiver?.accept(transformer, context)
|
||||
val arguments = translateCallArguments(expression, context, transformer)
|
||||
|
||||
// Transform external property accessor call
|
||||
// @JsName-annotated external property accessors are translated as function calls
|
||||
// Transform external and interface's property accessor call
|
||||
// @JsName-annotated external and interface's property accessors are translated as function calls
|
||||
if (function.getJsName() == null) {
|
||||
val property = function.correspondingPropertySymbol?.owner
|
||||
if (property != null && property.isEffectivelyExternal()) {
|
||||
if (
|
||||
property != null &&
|
||||
(property.isEffectivelyExternal() || property.isExportedInterfaceMember())
|
||||
) {
|
||||
val propertyName = context.getNameForProperty(property)
|
||||
val nameRef = when (jsDispatchReceiver) {
|
||||
null -> jsGlobalVarRef(JsNameRef(propertyName))
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.ir.backend.js.utils
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.isInterface
|
||||
import org.jetbrains.kotlin.descriptors.isClass
|
||||
import org.jetbrains.kotlin.ir.declarations.IrClass
|
||||
import org.jetbrains.kotlin.ir.declarations.IrDeclaration
|
||||
import org.jetbrains.kotlin.ir.util.parentClassOrNull
|
||||
|
||||
fun IrDeclaration?.isExportedClass() =
|
||||
this is IrClass && kind.isClass && isJsExport()
|
||||
|
||||
fun IrDeclaration?.isExportedInterface() =
|
||||
this is IrClass && kind.isInterface && isJsExport()
|
||||
|
||||
fun IrDeclaration.isExportedInterfaceMember() =
|
||||
parentClassOrNull.isExportedInterface()
|
||||
+1
-1
@@ -20,7 +20,7 @@ open class ExportedGenericClass<T>
|
||||
class <!NON_EXPORTABLE_TYPE("super; ExportedGenericClass<NonExportedClass>")!>ExportedClass3<!> : ExportedGenericClass<NonExportedClass>()
|
||||
|
||||
@JsExport
|
||||
interface <!WRONG_EXPORTED_DECLARATION!>ExportedGenericInterface<!><T>
|
||||
interface ExportedGenericInterface<T>
|
||||
|
||||
@JsExport
|
||||
class <!NON_EXPORTABLE_TYPE("super; ExportedGenericInterface<NonExportedClass>")!>ExportedClass4<!> : ExportedGenericInterface<NonExportedClass>
|
||||
|
||||
Vendored
+1
-1
@@ -14,4 +14,4 @@ fun <<!NON_EXPORTABLE_TYPE("upper bound; C")!>T : C<!>>foo() { }
|
||||
class A<<!NON_EXPORTABLE_TYPE("upper bound; C")!>T : C<!>, <!NON_EXPORTABLE_TYPE("upper bound; I")!>S: I<!>>
|
||||
|
||||
@JsExport
|
||||
interface <!WRONG_EXPORTED_DECLARATION!>I2<!><<!NON_EXPORTABLE_TYPE("upper bound; C"), NON_EXPORTABLE_TYPE("upper bound; I")!>T<!>> where T : C, T : I
|
||||
interface I2<<!NON_EXPORTABLE_TYPE("upper bound; C"), NON_EXPORTABLE_TYPE("upper bound; I")!>T<!>> where T : C, T : I
|
||||
|
||||
+13
-1
@@ -18,11 +18,23 @@ val String.extensionProperty<!>
|
||||
annotation class <!WRONG_EXPORTED_DECLARATION("annotation class")!>AnnotationClass<!>
|
||||
|
||||
@JsExport
|
||||
interface <!WRONG_EXPORTED_DECLARATION("interface")!>SomeInterface<!>
|
||||
interface SomeInterface
|
||||
|
||||
@JsExport
|
||||
external interface GoodInterface
|
||||
|
||||
@JsExport
|
||||
interface InterfaceWithCompanion {
|
||||
companion <!WRONG_EXPORTED_DECLARATION("companion object inside exported interface")!>object<!> {
|
||||
fun foo() = 42
|
||||
}
|
||||
}
|
||||
|
||||
@JsExport
|
||||
interface OuterInterface {
|
||||
class <!WRONG_EXPORTED_DECLARATION("nested class inside exported interface")!>Nested<!>
|
||||
}
|
||||
|
||||
@JsExport
|
||||
value class <!WRONG_EXPORTED_DECLARATION("value class")!>A(val a: Int)<!>
|
||||
|
||||
|
||||
+27
@@ -50,6 +50,33 @@ package foo {
|
||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
}
|
||||
|
||||
@kotlin.js.JsExport public interface InterfaceWithCompanion {
|
||||
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
|
||||
public companion object Companion {
|
||||
private constructor Companion()
|
||||
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public final fun foo(): kotlin.Int
|
||||
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
}
|
||||
}
|
||||
|
||||
@kotlin.js.JsExport public interface OuterInterface {
|
||||
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
|
||||
public final class Nested {
|
||||
public constructor Nested()
|
||||
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
}
|
||||
}
|
||||
|
||||
@kotlin.js.JsExport public interface SomeInterface {
|
||||
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||
|
||||
@@ -32,3 +32,6 @@ inline val ClassKind.isInterface: Boolean
|
||||
|
||||
inline val ClassKind.isEnumClass: Boolean
|
||||
get() = this == ClassKind.ENUM_CLASS
|
||||
|
||||
inline val ClassKind.isClass: Boolean
|
||||
get() = this == ClassKind.CLASS
|
||||
@@ -136,6 +136,11 @@ val DeclarationDescriptor.isInsidePrivateClass: Boolean
|
||||
return parent != null && DescriptorVisibilities.isPrivate(parent.visibility)
|
||||
}
|
||||
|
||||
val DeclarationDescriptor.isInsideInterface: Boolean
|
||||
get() {
|
||||
val parent = containingDeclaration as? ClassDescriptor
|
||||
return parent != null && parent.kind.isInterface
|
||||
}
|
||||
|
||||
fun ClassDescriptor.getSuperClassNotAny(): ClassDescriptor? {
|
||||
for (supertype in defaultType.constructor.supertypes) {
|
||||
|
||||
+8
-4
@@ -17,6 +17,7 @@ import org.jetbrains.kotlin.resolve.checkers.DeclarationChecker
|
||||
import org.jetbrains.kotlin.resolve.checkers.DeclarationCheckerContext
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.isEffectivelyExternal
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.isExtensionProperty
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.isInsideInterface
|
||||
import org.jetbrains.kotlin.resolve.inline.isInlineWithReified
|
||||
import org.jetbrains.kotlin.resolve.isInlineClass
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
@@ -109,12 +110,15 @@ object JsExportDeclarationChecker : DeclarationChecker {
|
||||
}
|
||||
|
||||
val wrongDeclaration: String? = when (descriptor.kind) {
|
||||
INTERFACE -> if (descriptor.isExternal) null else "interface"
|
||||
ANNOTATION_CLASS -> "annotation class"
|
||||
CLASS -> if (descriptor.isInlineClass()) {
|
||||
"${if (descriptor.isInline) "inline " else ""}${if (descriptor.isValue) "value " else ""}class"
|
||||
CLASS -> when {
|
||||
descriptor.isInsideInterface -> "nested class inside exported interface"
|
||||
descriptor.isInlineClass() -> "${if (descriptor.isInline) "inline " else ""}${if (descriptor.isValue) "value " else ""}class"
|
||||
else -> null
|
||||
}
|
||||
else -> if (descriptor.isInsideInterface) {
|
||||
"${if (descriptor.isCompanionObject) "companion object" else "nested/inner declaration"} inside exported interface"
|
||||
} else null
|
||||
else -> null
|
||||
}
|
||||
|
||||
if (wrongDeclaration != null) {
|
||||
|
||||
@@ -2070,6 +2070,12 @@ public class BoxJsTestGenerated extends AbstractBoxJsTest {
|
||||
runTest("js/js.translator/testData/box/export/exportEnumClass.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("exportInterface.kt")
|
||||
public void testExportInterface() throws Exception {
|
||||
runTest("js/js.translator/testData/box/export/exportInterface.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("exportNestedClass.kt")
|
||||
public void testExportNestedClass() throws Exception {
|
||||
|
||||
+6
@@ -2460,6 +2460,12 @@ public class IrBoxJsTestGenerated extends AbstractIrBoxJsTest {
|
||||
runTest("js/js.translator/testData/box/export/exportEnumClass.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("exportInterface.kt")
|
||||
public void testExportInterface() throws Exception {
|
||||
runTest("js/js.translator/testData/box/export/exportInterface.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("exportNestedClass.kt")
|
||||
public void testExportNestedClass() throws Exception {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// EXPECTED_REACHABLE_NODES: 1288
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
// IGNORE_BACKEND: JS_IR_ES6
|
||||
@JsExport
|
||||
interface I {
|
||||
val a: Char
|
||||
}
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
// IGNORE_BACKEND: JS
|
||||
// RUN_PLAIN_BOX_FUNCTION
|
||||
// INFER_MAIN_MODULE
|
||||
|
||||
// MODULE: export_interface
|
||||
// FILE: lib.kt
|
||||
|
||||
@JsExport
|
||||
interface I {
|
||||
val value: Int
|
||||
var variable: Int
|
||||
fun foo(): String
|
||||
}
|
||||
|
||||
open class NotExportedClass(override var value: Int) : I {
|
||||
override var variable: Int = value
|
||||
override open fun foo(): String = "Not Exported"
|
||||
}
|
||||
|
||||
@JsExport
|
||||
class ExportedClass(override val value: Int) : I {
|
||||
override var variable: Int = value
|
||||
override fun foo(): String = "Exported"
|
||||
}
|
||||
|
||||
@JsExport
|
||||
class AnotherOne : NotExportedClass(42) {
|
||||
override fun foo(): String = "Another One Exported"
|
||||
}
|
||||
|
||||
@JsExport
|
||||
fun generateNotExported(value: Int): NotExportedClass {
|
||||
return NotExportedClass(value)
|
||||
}
|
||||
|
||||
@JsExport
|
||||
fun consume(i: I): String {
|
||||
return "Value is ${i.value}, variable is ${i.variable} and result is '${i.foo()}'"
|
||||
}
|
||||
|
||||
// FILE: test.js
|
||||
function box() {
|
||||
const { I, ExportedClass, AnotherOne, generateNotExported, consume } = this["export_interface"]
|
||||
|
||||
if (I !== undefined) return "Fail: module should not export interface in runtime"
|
||||
|
||||
const exported = new ExportedClass(1)
|
||||
const another = new AnotherOne()
|
||||
const notExported = generateNotExported (3)
|
||||
|
||||
if (exported.foo() !== "Exported") return "Fail: foo function was not generated for ExportedClass"
|
||||
if (another.foo() !== "Another One Exported") return "Fail: foo function was not generated for AnotherOne"
|
||||
if (notExported.foo() !== "Not Exported") return "Fail: foo function was not generated for NotExportedClass"
|
||||
|
||||
if (exported.value !== 1) return "Fail: value getter was not generated for ExportedClass"
|
||||
if (another.value !== 42) return "Fail: value getter was not generated for AnotherOne"
|
||||
if (notExported.value !== 3) return "Fail: value getter was not generated for NotExportedClass"
|
||||
|
||||
if (exported.variable !== 1) return "Fail: variable getter was not generated for ExportedClass"
|
||||
if (another.variable !== 42) return "Fail: variable getter was not generated for AnotherOne"
|
||||
if (notExported.variable !== 3) return "Fail: variable getter was not generated for NotExportedClass"
|
||||
|
||||
exported.variable = 101
|
||||
another.variable = 102
|
||||
notExported.variable = 103
|
||||
|
||||
if (exported.variable !== 101) return "Fail: variable setter was not generated for ExportedClass"
|
||||
if (another.variable !== 102) return "Fail: variable setter was not generated for AnotherOne"
|
||||
if (notExported.variable !== 103) return "Fail: variable setter was not generated for NotExportedClass"
|
||||
|
||||
notExported.value = 42
|
||||
if (notExported.value !== 3) return "Fail: value setter was generated for NotExportedClass, but it shouldn't"
|
||||
|
||||
if (consume(exported) !== "Value is 1, variable is 101 and result is 'Exported'") return "Fail: methods or fields of ExportedClass was mangled"
|
||||
if (consume(another) !== "Value is 42, variable is 102 and result is 'Another One Exported'") return "Fail: methods or fields of AnotherOne was mangled"
|
||||
if (consume(notExported) !== "Value is 3, variable is 103 and result is 'Not Exported'") return "Fail: methods or fields of NotExported was mangled"
|
||||
|
||||
return "OK"
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
declare namespace JS_TESTS {
|
||||
type Nullable<T> = T | null | undefined
|
||||
const __doNotImplementIt: unique symbol
|
||||
type __doNotImplementIt = typeof __doNotImplementIt
|
||||
namespace foo {
|
||||
class TestInner {
|
||||
constructor(a: string);
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
declare namespace JS_TESTS {
|
||||
type Nullable<T> = T | null | undefined
|
||||
const __doNotImplementIt: unique symbol
|
||||
type __doNotImplementIt = typeof __doNotImplementIt
|
||||
class ClassWithDefaultCtor {
|
||||
constructor();
|
||||
readonly x: string;
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
declare namespace JS_TESTS {
|
||||
type Nullable<T> = T | null | undefined
|
||||
const __doNotImplementIt: unique symbol
|
||||
type __doNotImplementIt = typeof __doNotImplementIt
|
||||
namespace foo {
|
||||
const _val: number;
|
||||
let _var: number;
|
||||
@@ -123,5 +125,17 @@ declare namespace JS_TESTS {
|
||||
readonly prop: string;
|
||||
}
|
||||
}
|
||||
interface TestInterface {
|
||||
readonly value: string;
|
||||
getOwnerName(): string;
|
||||
readonly __doNotUseIt: __doNotImplementIt;
|
||||
}
|
||||
class TestInterfaceImpl implements foo.TestInterface {
|
||||
constructor(value: string);
|
||||
readonly value: string;
|
||||
getOwnerName(): string;
|
||||
readonly __doNotUseIt: __doNotImplementIt;
|
||||
}
|
||||
function processInterface(test: foo.TestInterface): string;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -179,4 +179,21 @@ enum class TestEnumClass(val constructorParameter: String) {
|
||||
class Nested {
|
||||
val prop: String = "hello2"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@JsExport
|
||||
interface TestInterface {
|
||||
val value: String
|
||||
fun getOwnerName(): String
|
||||
}
|
||||
|
||||
@JsExport
|
||||
class TestInterfaceImpl(override val value: String) : TestInterface {
|
||||
override fun getOwnerName() = "TestInterfaceImpl"
|
||||
}
|
||||
|
||||
@JsExport
|
||||
fun processInterface(test: TestInterface): String {
|
||||
return "Owner ${test.getOwnerName()} has value '${test.value}'"
|
||||
}
|
||||
+5
@@ -27,6 +27,8 @@ var TestSealed = JS_TESTS.foo.TestSealed;
|
||||
var TestAbstract = JS_TESTS.foo.TestAbstract;
|
||||
var TestDataClass = JS_TESTS.foo.TestDataClass;
|
||||
var TestEnumClass = JS_TESTS.foo.TestEnumClass;
|
||||
var TestInterfaceImpl = JS_TESTS.foo.TestInterfaceImpl;
|
||||
var processInterface = JS_TESTS.foo.processInterface;
|
||||
function assert(condition) {
|
||||
if (!condition) {
|
||||
throw "Assertion failed";
|
||||
@@ -112,5 +114,8 @@ function box() {
|
||||
assert(TestEnumClass.A.ordinal === 0);
|
||||
assert(TestEnumClass.B.ordinal === 1);
|
||||
assert(new TestEnumClass.Nested().prop == "hello2");
|
||||
assert(processInterface(new TestInterfaceImpl("bar")) == "Owner TestInterfaceImpl has value 'bar'");
|
||||
// @ts-expect-error "Just test that this code will throw compilation error for a user"
|
||||
assert(processInterface({ value: "bar", getOwnerName: function () { return "RandomObject"; } }) == "Owner RandomObject has value 'bar'");
|
||||
return "OK";
|
||||
}
|
||||
|
||||
+7
@@ -27,6 +27,8 @@ import TestSealed = JS_TESTS.foo.TestSealed;
|
||||
import TestAbstract = JS_TESTS.foo.TestAbstract;
|
||||
import TestDataClass = JS_TESTS.foo.TestDataClass;
|
||||
import TestEnumClass = JS_TESTS.foo.TestEnumClass;
|
||||
import TestInterfaceImpl = JS_TESTS.foo.TestInterfaceImpl;
|
||||
import processInterface = JS_TESTS.foo.processInterface;
|
||||
|
||||
function assert(condition: boolean) {
|
||||
if (!condition) {
|
||||
@@ -133,5 +135,10 @@ function box(): string {
|
||||
|
||||
assert(new TestEnumClass.Nested().prop == "hello2")
|
||||
|
||||
assert(processInterface(new TestInterfaceImpl("bar")) == "Owner TestInterfaceImpl has value 'bar'")
|
||||
|
||||
// @ts-expect-error "Just test that this code will throw compilation error for a user"
|
||||
assert(processInterface({ value: "bar", getOwnerName: () => "RandomObject" }) == "Owner RandomObject has value 'bar'")
|
||||
|
||||
return "OK";
|
||||
}
|
||||
+2
@@ -1,5 +1,7 @@
|
||||
declare namespace JS_TESTS {
|
||||
type Nullable<T> = T | null | undefined
|
||||
const __doNotImplementIt: unique symbol
|
||||
type __doNotImplementIt = typeof __doNotImplementIt
|
||||
namespace foo {
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
declare namespace JS_TESTS {
|
||||
type Nullable<T> = T | null | undefined
|
||||
const __doNotImplementIt: unique symbol
|
||||
type __doNotImplementIt = typeof __doNotImplementIt
|
||||
namespace foo {
|
||||
interface ExportedInterface {
|
||||
readonly __doNotUseIt: __doNotImplementIt;
|
||||
}
|
||||
function producer(value: number): any/* foo.NonExportedType */;
|
||||
function consumer(value: any/* foo.NonExportedType */): number;
|
||||
class A {
|
||||
@@ -14,5 +19,16 @@ declare namespace JS_TESTS {
|
||||
class C /* implements foo.NonExportedInterface */ {
|
||||
constructor();
|
||||
}
|
||||
class D implements foo.ExportedInterface/*, foo.NonExportedInterface */ {
|
||||
constructor();
|
||||
readonly __doNotUseIt: __doNotImplementIt;
|
||||
}
|
||||
class E /* extends foo.NonExportedType */ implements foo.ExportedInterface {
|
||||
constructor();
|
||||
readonly __doNotUseIt: __doNotImplementIt;
|
||||
}
|
||||
class F extends foo.A /* implements foo.NonExportedInterface */ {
|
||||
constructor();
|
||||
}
|
||||
}
|
||||
}
|
||||
+14
-2
@@ -11,6 +11,9 @@ package foo
|
||||
interface NonExportedInterface
|
||||
open class NonExportedType(val value: Int)
|
||||
|
||||
@JsExport
|
||||
interface ExportedInterface
|
||||
|
||||
@JsExport
|
||||
fun producer(value: Int): NonExportedType {
|
||||
return NonExportedType(value)
|
||||
@@ -22,7 +25,7 @@ fun consumer(value: NonExportedType): Int {
|
||||
}
|
||||
|
||||
@JsExport
|
||||
class A(var value: NonExportedType) {
|
||||
open class A(var value: NonExportedType) {
|
||||
fun <T: NonExportedType> increment(t: T): NonExportedType {
|
||||
return NonExportedType(value = t.value + 1)
|
||||
}
|
||||
@@ -32,4 +35,13 @@ class A(var value: NonExportedType) {
|
||||
class B(v: Int) : NonExportedType(v)
|
||||
|
||||
@JsExport
|
||||
class C : NonExportedInterface
|
||||
class C : NonExportedInterface
|
||||
|
||||
@JsExport
|
||||
class D : NonExportedInterface, ExportedInterface
|
||||
|
||||
@JsExport
|
||||
class E : NonExportedType(42), ExportedInterface
|
||||
|
||||
@JsExport
|
||||
class F : A(NonExportedType(42)), NonExportedInterface
|
||||
@@ -1,5 +1,7 @@
|
||||
declare namespace JS_TESTS {
|
||||
type Nullable<T> = T | null | undefined
|
||||
const __doNotImplementIt: unique symbol
|
||||
type __doNotImplementIt = typeof __doNotImplementIt
|
||||
namespace foo {
|
||||
interface I<T, S, U> {
|
||||
x: T;
|
||||
|
||||
+2
-2
@@ -26,12 +26,12 @@ var Impl = /** @class */ (function (_super) {
|
||||
};
|
||||
Object.defineProperty(Impl.prototype, "acAbstractProp", {
|
||||
get: function () { return "Impl"; },
|
||||
enumerable: true,
|
||||
enumerable: false,
|
||||
configurable: true
|
||||
});
|
||||
Object.defineProperty(Impl.prototype, "y", {
|
||||
get: function () { return true; },
|
||||
enumerable: true,
|
||||
enumerable: false,
|
||||
configurable: true
|
||||
});
|
||||
return Impl;
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
type Nullable<T> = T | null | undefined
|
||||
declare const __doNotImplementIt: unique symbol
|
||||
type __doNotImplementIt = typeof __doNotImplementIt
|
||||
export namespace foo {
|
||||
const prop: number;
|
||||
class C {
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
declare namespace JS_TESTS {
|
||||
type Nullable<T> = T | null | undefined
|
||||
const __doNotImplementIt: unique symbol
|
||||
type __doNotImplementIt = typeof __doNotImplementIt
|
||||
namespace foo {
|
||||
const prop: number;
|
||||
class C {
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
type Nullable<T> = T | null | undefined
|
||||
declare const __doNotImplementIt: unique symbol
|
||||
type __doNotImplementIt = typeof __doNotImplementIt
|
||||
export namespace foo {
|
||||
const prop: number;
|
||||
class C {
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
declare namespace JS_TESTS {
|
||||
type Nullable<T> = T | null | undefined
|
||||
const __doNotImplementIt: unique symbol
|
||||
type __doNotImplementIt = typeof __doNotImplementIt
|
||||
namespace foo.bar.baz {
|
||||
class C1 {
|
||||
constructor(value: string);
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
declare namespace JS_TESTS {
|
||||
type Nullable<T> = T | null | undefined
|
||||
const __doNotImplementIt: unique symbol
|
||||
type __doNotImplementIt = typeof __doNotImplementIt
|
||||
namespace foo {
|
||||
const _any: any;
|
||||
const _throwable: Error;
|
||||
|
||||
+2
@@ -1,5 +1,7 @@
|
||||
declare namespace JS_TESTS {
|
||||
type Nullable<T> = T | null | undefined
|
||||
const __doNotImplementIt: unique symbol
|
||||
type __doNotImplementIt = typeof __doNotImplementIt
|
||||
namespace foo {
|
||||
interface ExportedInternalInterface {
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
declare namespace JS_TESTS {
|
||||
type Nullable<T> = T | null | undefined
|
||||
const __doNotImplementIt: unique symbol
|
||||
type __doNotImplementIt = typeof __doNotImplementIt
|
||||
interface publicInterface {
|
||||
}
|
||||
const publicVal: number;
|
||||
|
||||
Reference in New Issue
Block a user