kotlin-suspend-transform-compiler-plugin icon indicating copy to clipboard operation
kotlin-suspend-transform-compiler-plugin copied to clipboard

Can't generate Typescript declarations for List<...>

Open kieranm opened this issue 2 months ago • 2 comments

Apologies if I'm doing something dumb here, I'm super new to this. I can't get it to export List<...> Typescript declarations correctly.

The plugin config is:


suspendTransformPlugin {
    transformers {
        addJsPromise {
            addCopyAnnotationExclude {
                // `kotlinJsExportIgnoreClassInfo` is a ClassInfo defined in advance.
                // You can also choose to use `packageName` and `className` directly.
                from(SuspendTransformConfigurations.kotlinJsExportIgnoreClassInfo)
            }
        }
    }
}

I have this code

...

@OptIn(ExperimentalJsExport::class)
@JsExport
data class Event(
    val id: String,
    val title: String,
    val startTs: Double,
    val endTs: Double
) {
    companion object {
        @JsExport.Ignore
        fun fromDb(e: DBEvent): Event =
            Event(e.id, e.title, e.start_ts, e.end_ts)
    }

    @JsExport.Ignore
    fun toDb(): DBEvent =
        DBEvent(id, title, startTs, endTs)
}

@OptIn(ExperimentalJsExport::class)
@JsExport
class EventsRepository(
    private val db: SyncDatabase
) {
    @JsExport.Ignore
    @JsPromise
    suspend fun listEvents(): List<Event> =
        db.eventQueries.listAll().awaitAsList().map(Event::fromDb)

    @JsPromise
    @JsExport.Ignore
    suspend fun getEventById(id: String): Event? =
        db.eventQueries.getById(id).awaitAsOneOrNull()?.let(Event::fromDb)
...
}

In the generated typescript, Promise<Any> is generated as the return type of listEventsAsync.

export declare class EventsRepository {
    constructor(db: any/* SyncDatabase */);
    getEventByIdAsync(id: string): Promise<Nullable<Event>>;
    updateTitleAsync(id: string, title: string): Promise<void>;
    listEventsAsync(): Promise<any/* KtList<Event> */>;
    insertAsync(id: string, title: string, startTs: number, endTs: number): Promise<void>;
    upsertAsync(id: string, title: string, startTs: number, endTs: number): Promise<void>;
    deleteAsync(id: string): Promise<void>;
}

If I add a test function as follows to the class, without any decorations...

fun test(): List<Event> {
        // No-op
        return emptyList()
    }

It exports the correct types, and also fixes the type in other places!

export declare class EventsRepository {
    constructor(db: any/* SyncDatabase */);
    test(): KtList<Event>;
    getEventByIdAsync(id: string): Promise<Nullable<Event>>;
    updateTitleAsync(id: string, title: string): Promise<void>;
    listEventsAsync(): Promise<KtList<Event>>;
    insertAsync(id: string, title: string, startTs: number, endTs: number): Promise<void>;
    upsertAsync(id: string, title: string, startTs: number, endTs: number): Promise<void>;
    deleteAsync(id: string): Promise<void>;
}

Would anybody be able to help me understand what I'm doing wrong here and how to fix this properly? Thanks!

kieranm avatar Sep 27 '25 16:09 kieranm