[JS IR] Add literal and union types to TS generation, support them for enum
^KT-37916 fixed ^KT-44494 fixed
This commit is contained in:
@@ -94,6 +94,11 @@ sealed class ExportedType {
|
||||
object Nothing : Primitive("never")
|
||||
}
|
||||
|
||||
sealed class LiteralType<T : Any>(val value: T) : ExportedType() {
|
||||
class StringLiteralType(value: String) : LiteralType<String>(value)
|
||||
class NumberLiteralType(value: Number) : LiteralType<Number>(value)
|
||||
}
|
||||
|
||||
class Array(val elementType: ExportedType) : ExportedType()
|
||||
class Function(
|
||||
val parameterTypes: List<ExportedType>,
|
||||
@@ -110,6 +115,8 @@ sealed class ExportedType {
|
||||
val members: List<ExportedDeclaration>
|
||||
) : ExportedType()
|
||||
|
||||
class UnionType(val lhs: ExportedType, val rhs: ExportedType) : ExportedType()
|
||||
|
||||
class IntersectionType(val lhs: ExportedType, val rhs: ExportedType) : ExportedType()
|
||||
|
||||
fun withNullability(nullable: Boolean) =
|
||||
|
||||
+89
-9
@@ -113,11 +113,18 @@ class ExportModelGenerator(
|
||||
}
|
||||
}
|
||||
|
||||
return exportPropertyUnsafely(property)
|
||||
}
|
||||
|
||||
private fun exportPropertyUnsafely(
|
||||
property: IrProperty,
|
||||
specializeType: ExportedType? = null
|
||||
): ExportedDeclaration {
|
||||
val parentClass = property.parent as? IrClass
|
||||
|
||||
return ExportedProperty(
|
||||
property.getExportedIdentifier(),
|
||||
exportType(property.getter!!.returnType),
|
||||
specializeType ?: exportType(property.getter!!.returnType),
|
||||
mutable = property.isVar,
|
||||
isMember = parentClass != null,
|
||||
isStatic = false,
|
||||
@@ -128,17 +135,44 @@ class ExportModelGenerator(
|
||||
)
|
||||
}
|
||||
|
||||
private fun exportEnumEntry(field: IrField): ExportedProperty? {
|
||||
if (field.origin != IrDeclarationOrigin.FIELD_FOR_ENUM_ENTRY) return null
|
||||
|
||||
private fun exportEnumEntry(field: IrField, enumEntries: List<IrEnumEntry>): ExportedProperty {
|
||||
val irEnumEntry = context.mapping.fieldToEnumEntry[field]
|
||||
?: error("Unable to find enum entry for ${field.fqNameWhenAvailable}")
|
||||
|
||||
val parentClass = field.parent as IrClass
|
||||
|
||||
val name = irEnumEntry.getExportedIdentifier()
|
||||
val ordinal = enumEntries.indexOf(irEnumEntry)
|
||||
|
||||
val nameProperty = ExportedProperty(
|
||||
name = "name",
|
||||
type = ExportedType.LiteralType.StringLiteralType(name),
|
||||
mutable = false,
|
||||
isMember = true,
|
||||
isStatic = false,
|
||||
isAbstract = false,
|
||||
isProtected = false,
|
||||
irGetter = null,
|
||||
irSetter = null,
|
||||
)
|
||||
val ordinalProperty = ExportedProperty(
|
||||
name = "ordinal",
|
||||
type = ExportedType.LiteralType.NumberLiteralType(ordinal),
|
||||
mutable = false,
|
||||
isMember = true,
|
||||
isStatic = false,
|
||||
isAbstract = false,
|
||||
isProtected = false,
|
||||
irGetter = null,
|
||||
irSetter = null,
|
||||
)
|
||||
val type = ExportedType.InlineInterfaceType(
|
||||
listOf(nameProperty, ordinalProperty)
|
||||
)
|
||||
|
||||
return ExportedProperty(
|
||||
name = irEnumEntry.getExportedIdentifier(),
|
||||
type = exportType(parentClass.defaultType),
|
||||
name = name,
|
||||
type = ExportedType.IntersectionType(exportType(parentClass.defaultType), type),
|
||||
mutable = false,
|
||||
isMember = true,
|
||||
isStatic = true,
|
||||
@@ -181,11 +215,25 @@ class ExportModelGenerator(
|
||||
|
||||
val members = mutableListOf<ExportedDeclaration>()
|
||||
val nestedClasses = mutableListOf<ExportedClass>()
|
||||
val enumEntries = if (klass.isEnumClass) {
|
||||
klass
|
||||
.declarations
|
||||
.filterIsInstance<IrField>()
|
||||
.mapNotNull { context.mapping.fieldToEnumEntry[it] }
|
||||
} else null
|
||||
|
||||
for (declaration in klass.declarations) {
|
||||
val candidate = getExportCandidate(declaration) ?: continue
|
||||
if (!shouldDeclarationBeExported(candidate, context)) continue
|
||||
|
||||
if (enumEntries != null) {
|
||||
val enumExportedMember = exportAsEnumMember(candidate, enumEntries)
|
||||
if (enumExportedMember != null) {
|
||||
members.add(enumExportedMember)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
when (candidate) {
|
||||
is IrSimpleFunction ->
|
||||
members.addIfNotNull(exportFunction(candidate))
|
||||
@@ -208,13 +256,11 @@ class ExportModelGenerator(
|
||||
is IrField -> {
|
||||
assert(
|
||||
candidate.origin == IrDeclarationOrigin.FIELD_FOR_OBJECT_INSTANCE ||
|
||||
candidate.origin == IrDeclarationOrigin.FIELD_FOR_ENUM_ENTRY
|
||||
|| candidate.origin == IrDeclarationOrigin.FIELD_FOR_OUTER_THIS
|
||||
candidate.origin == IrDeclarationOrigin.FIELD_FOR_OUTER_THIS
|
||||
|| candidate.correspondingPropertySymbol != null
|
||||
) {
|
||||
"Unexpected field without property ${candidate.fqNameWhenAvailable}"
|
||||
}
|
||||
members.addIfNotNull(exportEnumEntry(candidate))
|
||||
}
|
||||
|
||||
else -> error("Can't export member declaration $declaration")
|
||||
@@ -271,6 +317,40 @@ class ExportModelGenerator(
|
||||
)
|
||||
}
|
||||
|
||||
private fun exportAsEnumMember(candidate: IrDeclarationWithName, enumEntries: List<IrEnumEntry>): ExportedDeclaration? {
|
||||
return when (candidate) {
|
||||
is IrProperty -> {
|
||||
if (candidate.isEnumFakeOverriddenDeclaration(context)) {
|
||||
val type: ExportedType? = when (candidate.getExportedIdentifier()) {
|
||||
"name" -> enumEntries
|
||||
.map { it.getExportedIdentifier() }
|
||||
.map { ExportedType.LiteralType.StringLiteralType(it) }
|
||||
.reduce { acc: ExportedType, s: ExportedType -> ExportedType.UnionType(acc, s) }
|
||||
"ordinal" -> enumEntries
|
||||
.map { enumEntries.indexOf(it) }
|
||||
.map { ExportedType.LiteralType.NumberLiteralType(it) }
|
||||
.reduce { acc: ExportedType, s: ExportedType -> ExportedType.UnionType(acc, s) }
|
||||
else -> null
|
||||
}
|
||||
exportPropertyUnsafely(
|
||||
candidate,
|
||||
type
|
||||
)
|
||||
} else null
|
||||
}
|
||||
|
||||
is IrField -> {
|
||||
if (candidate.origin == IrDeclarationOrigin.FIELD_FOR_ENUM_ENTRY) {
|
||||
exportEnumEntry(candidate, enumEntries)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
private fun IrType.canBeUsedAsSuperTypeOfExportedClasses(): Boolean =
|
||||
!this.isAny() && classifierOrNull != context.irBuiltIns.enumClass
|
||||
|
||||
|
||||
+5
@@ -230,4 +230,9 @@ fun ExportedType.toTypeScript(indent: String): String = when (this) {
|
||||
is ExportedType.IntersectionType -> {
|
||||
lhs.toTypeScript(indent) + " & " + rhs.toTypeScript(indent)
|
||||
}
|
||||
is ExportedType.UnionType -> {
|
||||
lhs.toTypeScript(indent) + " | " + rhs.toTypeScript(indent)
|
||||
}
|
||||
is ExportedType.LiteralType.StringLiteralType -> "\"$value\""
|
||||
is ExportedType.LiteralType.NumberLiteralType -> value.toString()
|
||||
}
|
||||
+10
-4
@@ -101,15 +101,21 @@ declare namespace JS_TESTS {
|
||||
class TestEnumClass {
|
||||
private constructor();
|
||||
readonly constructorParameter: string;
|
||||
static readonly A: foo.TestEnumClass;
|
||||
static readonly B: foo.TestEnumClass;
|
||||
static readonly A: foo.TestEnumClass & {
|
||||
readonly name: "A";
|
||||
readonly ordinal: 0;
|
||||
};
|
||||
static readonly B: foo.TestEnumClass & {
|
||||
readonly name: "B";
|
||||
readonly ordinal: 1;
|
||||
};
|
||||
readonly foo: number;
|
||||
bar(value: string): string;
|
||||
bay(): string;
|
||||
static values(): Array<foo.TestEnumClass>;
|
||||
static valueOf(value: string): foo.TestEnumClass;
|
||||
readonly name: string;
|
||||
readonly ordinal: number;
|
||||
readonly name: "A" | "B";
|
||||
readonly ordinal: 0 | 1;
|
||||
}
|
||||
namespace TestEnumClass {
|
||||
class Nested {
|
||||
|
||||
Reference in New Issue
Block a user