Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Class ENUMs incorrectly output as union type when using mapEnum asEnum #1055

Open
garbit opened this issue Mar 26, 2024 · 1 comment
Open

Comments

@garbit
Copy link

garbit commented Mar 26, 2024

Hi there,

I have a kotlin enum in my data class file that seems to output a string union type in my TS implementation file as well as an enum definition. I was expecting my interface to use the enum rather than the string union type. How can I configure the typescript generation step to achieve this?

Enum:

import com.fasterxml.jackson.annotation.JsonValue

enum class ContentItemType(
    @JsonValue
    val id: Int,
    val pathPrefix: String
) {
    THING(id = 0, pathPrefix = "content_thing"),

    companion object {
        fun fromId(id: Int): ContentItemType {
            return entries.find { it.id == id } ?: THING
        }
    }
}

Class:

import java.util.*

data class ThingContentItem(
    override val type: ContentItemType = ContentItemType.THING,
    val title: String,
): DataContentItem

DataContentItem

import com.fasterxml.jackson.annotation.JsonSubTypes
import com.fasterxml.jackson.annotation.JsonTypeInfo

@JsonTypeInfo(
    use = JsonTypeInfo.Id.NAME,
    include = JsonTypeInfo.As.EXISTING_PROPERTY,
    property = "type"
)
@JsonSubTypes(
    value = [
        JsonSubTypes.Type(value = ThingContentItemDto::class, name = "0"),
    ]
)
sealed interface DataContentItem {
    val type: ContentItemType
}

I use the following settings in my gradle file:

tasks {
    generateTypeScript {
        jsonLibrary = JsonLibrary.jackson2
        outputKind = TypeScriptOutputKind.module
        outputFileType = TypeScriptFileType.implementationFile
        classPatterns = listOf("my.package.here.**")
        excludeClassPatterns = listOf("**Companion")
        outputFile = "[outputlocation]"
        mapEnum = EnumMapping.asEnum
        sortTypeDeclarations = true
    }
}

My Typescript output looks like this:

export const enum ContentItemType {
    THING = 0
}

export interface ThingDataContentDto extends DataContentItem {
    type: '0'; # <- I was expecting this to be an ENUM and not a union
    title: string;
}

I was expecting the ThingDataContentDto would use the ContentItemType.THING enum.

How can I achieve this / what am I doing wrong with the configuration?

@garbit
Copy link
Author

garbit commented Apr 12, 2024

Any updates on this?

My only solution so far is to perform a regex on the resulting ts file:

doLast {
    val file = File(project.projectDir, "kotlin-dtos.ts")
    if (!file.exists()) {
        throw RuntimeException("File not found: ${file.absolutePath}")
    }
    
    var content = file.readText()
    
    // Enum mapping from TypeScript number to enum
    val mapping = mapOf(
        "0" to "ContentItemType.THING",
    )
    
    // Regex patterns to find and replace types
    val patterns = listOf(
        Regex("""(?<=interface DataContentItem \{\s+type: )("[0-9]+"(?:\s*\|\s*"[0-9]+")*;)"""),
    )
    
    patterns.forEach { regex ->
        content = content.replace(regex) { matchResult ->
            matchResult.value
                .removeSurrounding("\"", ";")
                .split(" | ")
                .joinToString(" | ") { num -> mapping[num.trim('"')] ?: "ContentItemType.UNKNOWN" } + ";"
        }
    }
    
    file.writeText(content)
    println("Updated TypeScript definitions in ${file.absolutePath}")
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant