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 874264884ae..b4419df4d7f 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 @@ -27,7 +27,7 @@ data class ExportedFunction( val name: String, val returnType: ExportedType, val parameters: List, - val typeParameters: List = emptyList(), + val typeParameters: List = emptyList(), val isMember: Boolean = false, val isStatic: Boolean = false, val isAbstract: Boolean = false, @@ -111,7 +111,7 @@ sealed class ExportedType { ) : ExportedType() class ClassType(val name: String, val arguments: List, val ir: IrClass) : ExportedType() - class TypeParameter(val name: String) : ExportedType() + class TypeParameter(val name: String, val constraint: ExportedType? = null) : ExportedType() class Nullable(val baseType: ExportedType) : ExportedType() class ErrorType(val comment: String) : ExportedType() class TypeOf(val name: String) : 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 b5e7a7ca8a1..8a001c934ad 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 @@ -83,7 +83,7 @@ class ExportModelGenerator( function.getExportedIdentifier(), returnType = exportType(function.returnType), parameters = (listOfNotNull(function.extensionReceiverParameter) + function.valueParameters).map { exportParameter(it) }, - typeParameters = function.typeParameters.map { it.name.identifier }, + typeParameters = function.typeParameters.map(::exportTypeParameter), isMember = parent is IrClass, isStatic = function.isStaticMethodOfClass, isAbstract = parent is IrClass && !parent.isInterface && function.modality == Modality.ABSTRACT, @@ -441,6 +441,25 @@ class ExportModelGenerator( return ExportedType.ErrorType("UnknownType ${type.render()}") } + private fun exportTypeParameter(typeParameter: IrTypeParameter): ExportedType.TypeParameter { + val constraint = typeParameter.superTypes.asSequence() + .filter { it != context.irBuiltIns.anyNType } + .map(::exportType) + .filter { it !is ExportedType.ErrorType && it !is ExportedType.ImplicitlyExportedType } + .toList() + + return ExportedType.TypeParameter( + typeParameter.name.identifier, + constraint.run { + when (size) { + 0 -> null + 1 -> single() + else -> reduce(ExportedType::IntersectionType) + } + } + ) + } + private fun exportType(type: IrType): ExportedType { if (type is IrDynamicType) return ExportedType.Primitive.Any 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 ca466d4af4b..beb4f53656f 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 @@ -77,7 +77,7 @@ fun ExportedDeclaration.toTypeScript(indent: String, prefix: String = ""): Strin val renderedTypeParameters = if (typeParameters.isNotEmpty()) - "<" + typeParameters.joinToString(", ") + ">" + "<" + typeParameters.joinToString(", ") { it.toTypeScript(indent) } + ">" else "" @@ -285,7 +285,6 @@ fun ExportedType.toTypeScript(indent: String): String = when (this) { is ExportedType.TypeOf -> "typeof $name" - is ExportedType.TypeParameter -> name is ExportedType.ErrorType -> "any /*$comment*/" is ExportedType.Nullable -> "Nullable<" + baseType.toTypeScript(indent) + ">" is ExportedType.InlineInterfaceType -> { @@ -302,4 +301,9 @@ fun ExportedType.toTypeScript(indent: String): String = when (this) { is ExportedType.ImplicitlyExportedType -> { ExportedType.Primitive.Any.toTypeScript(indent) + "/* ${type.toTypeScript("")} */" } + is ExportedType.TypeParameter -> if (constraint == null) { + name + } else { + "$name extends ${constraint.toTypeScript(indent)}" + } } \ No newline at end of file diff --git a/js/js.translator/testData/typescript-export/declarations/declarations.d.ts b/js/js.translator/testData/typescript-export/declarations/declarations.d.ts index fec9af202de..dfbf5a06e27 100644 --- a/js/js.translator/testData/typescript-export/declarations/declarations.d.ts +++ b/js/js.translator/testData/typescript-export/declarations/declarations.d.ts @@ -16,6 +16,8 @@ declare namespace JS_TESTS { function defaultParameters(x: number, y: string): string; function generic1(x: T): T; function generic2(x: Nullable): boolean; + function genericWithConstraint(x: T): T; + function genericWithMultipleConstraints(x: T): T; function generic3(a: A, b: B, c: C, d: D): Nullable; function inlineFun(x: number, callback: (p0: number) => void): void; const _const_val: number; diff --git a/js/js.translator/testData/typescript-export/declarations/declarations.kt b/js/js.translator/testData/typescript-export/declarations/declarations.kt index 66ab80da281..b552e8c7cbe 100644 --- a/js/js.translator/testData/typescript-export/declarations/declarations.kt +++ b/js/js.translator/testData/typescript-export/declarations/declarations.kt @@ -44,6 +44,15 @@ fun generic1(x: T): T = x @JsExport fun generic2(x: T?): Boolean = (x == null) +@JsExport +fun genericWithConstraint(x: T): T = x + +@JsExport +fun genericWithMultipleConstraints(x: T): T + where T : Comparable, + T : TestInterface, + T : Throwable = x + @JsExport fun generic3(a: A, b: B, c: C, d: D): E? = null