[K/JS] fix(TS): save type hierarchy for exported classes.

This commit is contained in:
Artem Kobzar
2022-08-03 14:16:30 +00:00
committed by Space
parent ce6c8a51ba
commit b9a80284c8
12 changed files with 256 additions and 72 deletions
-3
View File
@@ -8,7 +8,4 @@
<component name="KotlinCompilerSettings">
<option name="additionalArguments" value="-version -Xallow-kotlin-package -Xskip-metadata-version-check" />
</component>
<component name="KotlinJpsPluginSettings">
<option name="version" value="1.8.0-dev-1006" />
</component>
</project>
@@ -70,7 +70,7 @@ sealed class ExportedClass : ExportedDeclaration() {
abstract val name: String
abstract val ir: IrClass
abstract val members: List<ExportedDeclaration>
abstract val superClass: ExportedType?
abstract val superClasses: List<ExportedType>
abstract val superInterfaces: List<ExportedType>
abstract val nestedClasses: List<ExportedClass>
}
@@ -79,7 +79,7 @@ data class ExportedRegularClass(
override val name: String,
val isInterface: Boolean = false,
val isAbstract: Boolean = false,
override val superClass: ExportedType? = null,
override val superClasses: List<ExportedType> = emptyList(),
override val superInterfaces: List<ExportedType> = emptyList(),
val typeParameters: List<ExportedType.TypeParameter>,
override val members: List<ExportedDeclaration>,
@@ -89,7 +89,7 @@ data class ExportedRegularClass(
data class ExportedObject(
override val name: String,
override val superClass: ExportedType? = null,
override val superClasses: List<ExportedType> = emptyList(),
override val superInterfaces: List<ExportedType> = emptyList(),
override val members: List<ExportedDeclaration>,
override val nestedClasses: List<ExportedClass>,
@@ -115,6 +115,7 @@ sealed class ExportedType {
object String : Primitive("string")
object Throwable : Primitive("Error")
object Any : Primitive("any")
object Unknown : Primitive("unknown")
object Unit : Primitive("void")
object Nothing : Primitive("never")
object UniqueSymbol : Primitive("unique symbol")
@@ -147,16 +148,16 @@ sealed class ExportedType {
class PropertyType(val container: ExportedType, val propertyName: ExportedType) : ExportedType()
class ImplicitlyExportedType(val type: ExportedType) : ExportedType() {
data class ImplicitlyExportedType(val type: ExportedType, val exportedSupertype: ExportedType) : ExportedType() {
override fun withNullability(nullable: Boolean) =
ImplicitlyExportedType(type.withNullability(nullable))
ImplicitlyExportedType(type.withNullability(nullable), exportedSupertype.withNullability(nullable))
}
open fun withNullability(nullable: Boolean) =
if (nullable) Nullable(this) else this
fun withImplicitlyExported(implicitlyExportedType: Boolean) =
if (implicitlyExportedType) ImplicitlyExportedType(this) else this
fun withImplicitlyExported(implicitlyExportedType: Boolean, exportedSupertype: ExportedType) =
if (implicitlyExportedType) ImplicitlyExportedType(this, exportedSupertype) else this
}
enum class ExportedVisibility(val keyword: String) {
@@ -24,14 +24,14 @@ import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.serialization.js.ModuleKind
import org.jetbrains.kotlin.utils.addIfNotNull
import org.jetbrains.kotlin.utils.addToStdlib.runIf
import org.jetbrains.kotlin.utils.keysToMap
private const val magicPropertyName = "__doNotUseOrImplementIt"
class ExportModelGenerator(
val context: JsIrBackendContext,
val generateNamespacesForPackages: Boolean
) {
class ExportModelGenerator(val context: JsIrBackendContext, val generateNamespacesForPackages: Boolean) {
private val transitiveExportCollector = TransitiveExportCollector(context)
fun generateExport(file: IrPackageFragment): List<ExportedDeclaration> {
val namespaceFqName = file.fqName
val exports = file.declarations.flatMap { declaration -> listOfNotNull(exportDeclaration(declaration)) }
@@ -64,10 +64,14 @@ class ExportModelGenerator(
}
}
private fun exportClass(candidate: IrClass) = if (candidate.isEnumClass) {
exportEnumClass(candidate)
} else {
exportOrdinaryClass(candidate)
private fun exportClass(candidate: IrClass): ExportedDeclaration? {
val superTypes = candidate.defaultType.collectSuperTransitiveHierarchy() + candidate.superTypes
return if (candidate.isEnumClass) {
exportEnumClass(candidate, superTypes)
} else {
exportOrdinaryClass(candidate, superTypes)
}
}
private fun exportFunction(function: IrSimpleFunction): ExportedDeclaration? {
@@ -203,9 +207,7 @@ class ExportModelGenerator(
return Exportability.Allowed
}
private fun exportOrdinaryClass(
klass: IrClass
): ExportedDeclaration? {
private fun exportOrdinaryClass(klass: IrClass, superTypes: Iterable<IrType>): ExportedDeclaration? {
when (val exportability = classExportability(klass)) {
is Exportability.Prohibited -> error(exportability.reason)
is Exportability.NotNeeded -> return null
@@ -213,18 +215,17 @@ class ExportModelGenerator(
}
}
val (members, nestedClasses) = exportClassDeclarations(klass)
val (members, nestedClasses) = exportClassDeclarations(klass, superTypes)
return exportClass(
klass,
superTypes,
members,
nestedClasses
)
}
private fun exportEnumClass(
klass: IrClass
): ExportedDeclaration? {
private fun exportEnumClass(klass: IrClass, superTypes: Iterable<IrType>): ExportedDeclaration? {
when (val exportability = classExportability(klass)) {
is Exportability.Prohibited -> error(exportability.reason)
is Exportability.NotNeeded -> return null
@@ -241,7 +242,7 @@ class ExportModelGenerator(
enumEntries
.keysToMap(enumEntries::indexOf)
val (members, nestedClasses) = exportClassDeclarations(klass) { candidate ->
val (members, nestedClasses) = exportClassDeclarations(klass, superTypes) { candidate ->
val enumExportedMember = exportAsEnumMember(candidate, enumEntriesToOrdinal)
enumExportedMember
}
@@ -253,15 +254,15 @@ class ExportModelGenerator(
return exportClass(
klass,
superTypes,
listOf(privateConstructor) + members,
nestedClasses
).let {
(it as ExportedRegularClass).copy(isAbstract = true)
}
)
}
private fun exportClassDeclarations(
klass: IrClass,
superTypes: Iterable<IrType>,
specialProcessing: (IrDeclarationWithName) -> ExportedDeclaration? = { null }
): ExportedClassDeclarationsInfo {
val members = mutableListOf<ExportedDeclaration>()
@@ -310,8 +311,8 @@ class ExportModelGenerator(
}
}
if (klass.shouldContainImplementationOfMagicProperty()) {
members.addMagicPropertyForInterfaceImplementation(klass)
if (klass.shouldContainImplementationOfMagicProperty(superTypes)) {
members.addMagicPropertyForInterfaceImplementation(klass, superTypes)
} else if (klass.shouldNotBeImplemented()) {
members.addMagicInterfaceProperty(klass)
}
@@ -326,7 +327,7 @@ class ExportModelGenerator(
return isInterface && !isExternal
}
private fun IrClass.shouldContainImplementationOfMagicProperty(): Boolean {
private fun IrClass.shouldContainImplementationOfMagicProperty(superTypes: Iterable<IrType>): Boolean {
return !isExternal && superTypes.any {
val superClass = it.classOrNull?.owner ?: return@any false
superClass.isInterface && superClass.isExported(context)
@@ -337,8 +338,8 @@ class ExportModelGenerator(
add(ExportedProperty(name = magicPropertyName, type = klass.generateTagType(), mutable = false, isMember = true, isField = true))
}
private fun MutableList<ExportedDeclaration>.addMagicPropertyForInterfaceImplementation(klass: IrClass) {
val superTypesToInheritanceMagicProperty = klass.superTypes.filter { it.shouldAddMagicPropertyOfSuper() }
private fun MutableList<ExportedDeclaration>.addMagicPropertyForInterfaceImplementation(klass: IrClass, superTypes: Iterable<IrType>) {
val superTypesToInheritanceMagicProperty = superTypes.filter { it.shouldAddMagicPropertyOfSuper() }
if (superTypesToInheritanceMagicProperty.isEmpty()) return
@@ -375,20 +376,20 @@ class ExportModelGenerator(
private fun exportClass(
klass: IrClass,
superTypes: Iterable<IrType>,
members: List<ExportedDeclaration>,
nestedClasses: List<ExportedClass>
nestedClasses: List<ExportedClass>,
): ExportedDeclaration {
val typeParameters = klass.typeParameters.map(::exportTypeParameter)
// TODO: Handle non-exported super types
val superClasses = superTypes
.filter { !it.classifierOrFail.isInterface && it.canBeUsedAsSuperTypeOfExportedClasses() }
.map { exportType(it, false) }
.filter { it !is ExportedType.ErrorType }
val superType = klass.superTypes
.firstOrNull { !it.classifierOrFail.isInterface && it.canBeUsedAsSuperTypeOfExportedClasses() }
?.let { exportType(it).takeIf { it !is ExportedType.ErrorType } }
val superInterfaces = klass.superTypes
val superInterfaces = superTypes
.filter { it.classifierOrFail.isInterface }
.map { exportType(it) }
.map { exportType(it, false) }
.filter { it !is ExportedType.ErrorType }
val name = klass.getExportedIdentifier()
@@ -398,7 +399,7 @@ class ExportModelGenerator(
ir = klass,
name = name,
members = members,
superClass = superType,
superClasses = superClasses,
nestedClasses = nestedClasses,
superInterfaces = superInterfaces,
irGetter = context.mapping.objectToGetInstanceFunction[klass]!!
@@ -407,8 +408,8 @@ class ExportModelGenerator(
ExportedRegularClass(
name = name,
isInterface = klass.isInterface,
isAbstract = klass.modality == Modality.ABSTRACT || klass.modality == Modality.SEALED,
superClass = superType,
isAbstract = klass.modality == Modality.ABSTRACT || klass.modality == Modality.SEALED || klass.isEnumClass,
superClasses = superClasses,
superInterfaces = superInterfaces,
typeParameters = typeParameters,
members = members,
@@ -418,6 +419,9 @@ class ExportModelGenerator(
}
}
private fun IrSimpleType.collectSuperTransitiveHierarchy() =
transitiveExportCollector.collectSuperTypesTransitiveHierarchyFor(this)
private fun exportAsEnumMember(
candidate: IrDeclarationWithName,
enumEntriesToOrdinal: Map<IrEnumEntry, Int>
@@ -471,8 +475,15 @@ class ExportModelGenerator(
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 }
.map {
val exportedType = exportType(it)
if (exportedType is ExportedType.ImplicitlyExportedType && exportedType.exportedSupertype == ExportedType.Primitive.Any) {
exportedType.copy(exportedSupertype = ExportedType.Primitive.Unknown)
} else {
exportedType
}
}
.filter { it !is ExportedType.ErrorType }
.toList()
return ExportedType.TypeParameter(
@@ -487,7 +498,7 @@ class ExportModelGenerator(
)
}
private fun exportType(type: IrType): ExportedType {
private fun exportType(type: IrType, shouldCalculateExportedSupertypeForImplicit: Boolean = true): ExportedType {
if (type is IrDynamicType)
return ExportedType.Primitive.Any
@@ -535,6 +546,12 @@ class ExportModelGenerator(
val isNonExportedExternal = klass.isExternal && !isExported
val name = klass.getFqNameWithJsNameWhenAvailable(!isNonExportedExternal && generateNamespacesForPackages).asString()
val exportedSupertype = runIf(shouldCalculateExportedSupertypeForImplicit && isImplicitlyExported) {
val transitiveExportedType = nonNullType.collectSuperTransitiveHierarchy()
if (transitiveExportedType.isEmpty()) return@runIf null
transitiveExportedType.map(::exportType).reduce(ExportedType::IntersectionType)
} ?: ExportedType.Primitive.Any
when (klass.kind) {
ClassKind.ANNOTATION_CLASS,
ClassKind.ENUM_ENTRY ->
@@ -550,7 +567,7 @@ class ExportModelGenerator(
type.arguments.map { exportTypeArgument(it) },
klass
)
}.withImplicitlyExported(isImplicitlyExported)
}.withImplicitlyExported(isImplicitlyExported, exportedSupertype)
}
else -> error("Unexpected classifier $classifier")
@@ -683,7 +700,11 @@ private fun shouldDeclarationBeExported(declaration: IrDeclarationWithName, cont
}
fun IrOverridableDeclaration<*>.isAllowedFakeOverriddenDeclaration(context: JsIrBackendContext): Boolean {
if (this.resolveFakeOverride(allowAbstract = true)?.parentClassOrNull.isExportedInterface(context)) {
val firstExportedRealOverride = runIf(isFakeOverride) {
resolveFakeOverrideOrNull(allowAbstract = true) { it === this || it.parentClassOrNull?.isExported(context) != true }
}
if (firstExportedRealOverride?.parentClassOrNull.isExportedInterface(context)) {
return true
}
@@ -14,6 +14,9 @@ import org.jetbrains.kotlin.ir.util.isObject
import org.jetbrains.kotlin.ir.util.parentAsClass
import org.jetbrains.kotlin.js.common.isValidES5Identifier
import org.jetbrains.kotlin.serialization.js.ModuleKind
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstance
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
import javax.lang.model.type.IntersectionType
private const val Nullable = "Nullable"
private const val objects = "_objects_"
@@ -176,10 +179,7 @@ class ExportModelToTsDeclarations {
var t: ExportedType = ExportedType.InlineInterfaceType(members)
if (superClass != null)
t = ExportedType.IntersectionType(t, superClass)
for (superInterface in superInterfaces) {
for (superInterface in superClasses + superInterfaces) {
t = ExportedType.IntersectionType(t, superInterface)
}
@@ -210,13 +210,13 @@ class ExportModelToTsDeclarations {
property.generateTypeScriptString(indent, prefix)
} else {
val propertyRef = "$objects.$propertyName"
val shouldCreateExtraProperty = members.isNotEmpty() || superInterfaces.isNotEmpty() || superClass != null
val shouldCreateExtraProperty = members.isNotEmpty() || superInterfaces.isNotEmpty() || superClasses.isNotEmpty()
val newSuperClass = ExportedType.ClassType(propertyRef, emptyList(), ir).takeIf { shouldCreateExtraProperty }
ExportedRegularClass(
name = name,
isInterface = false,
isAbstract = true,
superClass = newSuperClass,
superClasses = listOfNotNull(newSuperClass),
superInterfaces = superInterfaces,
typeParameters = emptyList(),
members = listOf(ExportedConstructor(emptyList(), ExportedVisibility.PRIVATE)),
@@ -232,7 +232,7 @@ class ExportModelToTsDeclarations {
val keyword = if (isInterface) "interface" else "class"
val superInterfacesKeyword = if (isInterface) "extends" else "implements"
val superClassClause = superClass?.let { it.toExtendsClause(indent) } ?: ""
val superClassClause = superClasses.toExtendsClause(indent)
val superInterfacesClause = superInterfaces.toImplementsClause(superInterfacesKeyword, indent)
val (memberObjects, nestedDeclarations) = nestedClasses.partition { it.couldBeProperty() }
@@ -279,12 +279,18 @@ class ExportModelToTsDeclarations {
return if (name.isValidES5Identifier()) klassExport + staticsExport else ""
}
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
private fun List<ExportedType>.toExtendsClause(indent: String): String {
if (isEmpty()) return ""
val implicitlyExportedClasses = filterIsInstance<ExportedType.ImplicitlyExportedType>()
val implicitlyExportedClassesString = implicitlyExportedClasses.joinToString(", ") { it.toTypeScript(indent, true) }
return if (implicitlyExportedClasses.count() == count()) {
" /* extends $implicitlyExportedClassesString */"
} else {
val originallyDefinedSuperClass = implicitlyExportedClassesString.takeIf { it.isNotEmpty() }?.let { "/* $it */ " }.orEmpty()
val transitivelyDefinedSuperClass = firstIsInstance<ExportedType.ClassType>().toTypeScript(indent, false)
" extends $originallyDefinedSuperClass$transitivelyDefinedSuperClass"
}
}
@@ -383,7 +389,12 @@ class ExportModelToTsDeclarations {
is ExportedType.LiteralType.NumberLiteralType -> value.toString()
is ExportedType.ImplicitlyExportedType -> {
val typeString = type.toTypeScript("", true)
if (isInCommentContext) typeString else ExportedType.Primitive.Any.toTypeScript(indent) + "/* $typeString */"
if (isInCommentContext) {
typeString
} else {
val superTypeString = exportedSupertype.toTypeScript(indent)
superTypeString.let { if (exportedSupertype is ExportedType.IntersectionType) "($it)" else it } + "/* $typeString */"
}
}
is ExportedType.PropertyType -> "${container.toTypeScript(indent, isInCommentContext)}[${
@@ -0,0 +1,65 @@
/*
* Copyright 2010-2022 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.export
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
import org.jetbrains.kotlin.ir.backend.js.lower.isBuiltInClass
import org.jetbrains.kotlin.ir.backend.js.lower.isStdLibClass
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.symbols.*
import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.util.*
private typealias SubstitutionMap = Map<IrTypeParameterSymbol, IrType>
class TransitiveExportCollector(val context: JsIrBackendContext) {
private val typesCaches = hashMapOf<ClassWithAppliedArguments, Set<IrType>>()
fun collectSuperTypesTransitiveHierarchyFor(type: IrSimpleType): Set<IrType> {
val classSymbol = type.classOrNull ?: return emptySet()
return typesCaches.getOrPut(ClassWithAppliedArguments(classSymbol, type.arguments)) {
type.collectSuperTypesTransitiveHierarchy(type.calculateTypeSubstitutionMap(emptyMap()))
}
}
private fun IrSimpleType.collectSuperTypesTransitiveHierarchy(typeSubstitutionMap: SubstitutionMap): Set<IrType> {
val classifier = classOrNull ?: return emptySet()
return classifier.superTypes()
.flatMap { (it as? IrSimpleType)?.collectTransitiveHierarchy(typeSubstitutionMap) ?: emptySet() }
.toSet()
}
private fun IrSimpleType.collectTransitiveHierarchy(typeSubstitutionMap: SubstitutionMap): Set<IrType> {
val owner = classifier.owner as? IrClass ?: return emptySet()
val substitutionMap = calculateTypeSubstitutionMap(typeSubstitutionMap)
return when {
isBuiltInClass(owner) || isStdLibClass(owner) -> emptySet()
owner.isExported(context) -> setOf(substitute(typeSubstitutionMap))
else -> collectSuperTypesTransitiveHierarchy(substitutionMap)
}
}
private fun IrTypeArgument.getSubstitution(typeSubstitutionMap: SubstitutionMap): IrType {
return when (this) {
is IrType -> substitute(typeSubstitutionMap)
is IrTypeProjection -> type.substitute(typeSubstitutionMap)
else -> error("Unexpected ir type argument")
}
}
private fun IrSimpleType.calculateTypeSubstitutionMap(typeSubstitutionMap: SubstitutionMap): SubstitutionMap {
val classifier = classOrNull ?: error("Unexpected classifier $classifier for collecting transitive hierarchy")
return typeSubstitutionMap + classifier.owner.typeParameters.zip(arguments).associate {
it.first.symbol to it.second.getSubstitution(typeSubstitutionMap)
}
}
private data class ClassWithAppliedArguments(val classSymbol: IrClassSymbol, val appliedArguments: List<IrTypeArgument>)
}
@@ -18,6 +18,7 @@ import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.isChildOf
private val BODILESS_BUILTIN_CLASSES = listOf(
"kotlin.String",
@@ -44,6 +45,9 @@ private val BODILESS_BUILTIN_CLASSES = listOf(
fun isBuiltInClass(declaration: IrDeclaration): Boolean =
declaration is IrClass && declaration.fqNameWhenAvailable in BODILESS_BUILTIN_CLASSES
fun isStdLibClass(declaration: IrDeclaration): Boolean =
declaration is IrClass && declaration.fqNameWhenAvailable?.isChildOf(JsPackage) != false
private val JsPackage = FqName("kotlin.js")
private val JsIntrinsicFqName = FqName("kotlin.js.JsIntrinsic")
@@ -93,16 +93,24 @@ fun Collection<IrOverridableMember>.collectAndFilterRealOverrides(): Set<IrOverr
else -> error("all members should be of the same kind, got ${map { it.render() }}")
}
// TODO: use this implementation instead of any other
fun <S : IrSymbol, T : IrOverridableDeclaration<S>> T.resolveFakeOverride(
allowAbstract: Boolean = false,
toSkip: (T) -> Boolean = { false }
): T? =
resolveFakeOverrideOrNull(allowAbstract, toSkip).also {
if (allowAbstract && it == null) {
error("No real overrides for ${this.render()}")
}
}
// TODO: use this implementation instead of any other
fun <S : IrSymbol, T : IrOverridableDeclaration<S>> T.resolveFakeOverrideOrNull(
allowAbstract: Boolean = false,
toSkip: (T) -> Boolean = { false }
): T? {
if (!isFakeOverride && !toSkip(this)) return this
return if (allowAbstract) {
val reals = collectRealOverrides(toSkip)
if (reals.isEmpty()) error("No real overrides for ${this.render()}")
reals.first()
collectRealOverrides(toSkip).firstOrNull()
} else {
collectRealOverrides(toSkip, { it.modality == Modality.ABSTRACT })
.let { realOverrides ->
@@ -11,8 +11,8 @@ declare namespace JS_TESTS {
function generic1<T>(x: T): T;
function generic2<T>(x: Nullable<T>): boolean;
function genericWithConstraint<T extends string>(x: T): T;
function genericWithMultipleConstraints<T extends RegExpMatchArray & Error>(x: T): T;
function genericWithMultipleConstraints<T extends unknown/* kotlin.Comparable<T> */ & RegExpMatchArray & Error>(x: T): T;
function generic3<A, B, C, D, E>(a: A, b: B, c: C, d: D): Nullable<E>;
function inlineFun(x: number, callback: (p0: number) => void): void;
}
}
}
@@ -12,7 +12,7 @@ declare namespace JS_TESTS {
constructor(value: any/* foo.NonExportedType */);
get value(): any/* foo.NonExportedType */;
set value(value: any/* foo.NonExportedType */);
increment<T>(t: T): any/* foo.NonExportedType */;
increment<T extends unknown/* foo.NonExportedType */>(t: T): any/* foo.NonExportedType */;
}
class B /* extends foo.NonExportedType */ {
constructor(v: number);
@@ -13,6 +13,7 @@ declare namespace JS_TESTS {
}
}
namespace foo {
const fifth: (foo.Third<boolean> & foo.IA & foo.IG<boolean>)/* foo.Fifth<boolean> */;
abstract class AC implements foo.I2 {
constructor();
get x(): string;
@@ -100,5 +101,31 @@ declare namespace JS_TESTS {
abstract get baz(): string;
readonly __doNotUseOrImplementIt: foo.I3["__doNotUseOrImplementIt"];
}
interface IA {
readonly foo: any;
readonly __doNotUseOrImplementIt: {
readonly "foo.IA": unique symbol;
};
}
interface IG<T> {
process(value: T): void;
readonly __doNotUseOrImplementIt: {
readonly "foo.IG": unique symbol;
};
}
class Third<T> extends /* foo.Second */ foo.First {
constructor();
}
class Sixth extends /* foo.Fifth<number> */ foo.Third<number> implements foo.IA, foo.IG<number>/*, foo.IC */ {
constructor();
process(value: number): void;
get foo(): number;
readonly __doNotUseOrImplementIt: foo.IA["__doNotUseOrImplementIt"] & foo.IG<number>["__doNotUseOrImplementIt"];
}
class First {
constructor();
}
function acceptForthLike<T extends (foo.Third<string> & foo.IA)/* foo.Forth<string> */>(forth: T): void;
function acceptMoreGenericForthLike<T extends foo.IA/* foo.IB */ & foo.IA/* foo.IC */ & foo.First/* foo.Second */>(forth: T): void;
}
}
@@ -163,3 +163,53 @@ enum class EC : I3 {
override fun bay(): String = "bay"
}
// Save hierarhy
@JsExport
interface IA {
val foo: Any
}
@JsExport
interface IG<T> {
fun process(value: T): Unit
}
interface IB : IA
interface IC : IB {
override val foo: Any
}
interface ID : IC {
override val foo: Int
}
@JsExport
open class Third<T>: Second()
open class Forth<A>: Third<A>(), IB, ID {
override val foo: Int = 42
}
open class Fifth<B>: Forth<B>(), IG<B> {
override fun process(value: B) {}
}
@JsExport
class Sixth: Fifth<Int>(), IC
@JsExport
open class First
open class Second: First()
@JsExport
fun <T : Forth<String>> acceptForthLike(forth: T) {}
@JsExport
fun <T> acceptMoreGenericForthLike(forth: T) where T: IB, T: IC, T: Second {}
@JsExport
val fifth = Fifth<Boolean>()
@@ -13,7 +13,7 @@ declare namespace JS_TESTS {
generic1<T>(x: T): T;
generic2<T>(x: Nullable<T>): boolean;
genericWithConstraint<T extends string>(x: T): T;
genericWithMultipleConstraints<T extends RegExpMatchArray & Error>(x: T): T;
genericWithMultipleConstraints<T extends unknown/* kotlin.Comparable<T> */ & RegExpMatchArray & Error>(x: T): T;
generic3<A, B, C, D, E>(a: A, b: B, c: C, d: D): Nullable<E>;
inlineFun(x: number, callback: (p0: number) => void): void;
}