在 Kotlin 里面引用 TypeScript 函数,然后还有一些其他常用的东西
基本
@JsModule("YourModule")是 extern 的核心,这里面你的 module 一定是要直接有代码的,类似
1 | declare namespace { |
而不是
1 | import {xx} from 'YourModule' |
否则会出现找不到 Module 的问题
Enum Class
1 | declare enum E { |
很明显,f 返回的其实就是 Number,转换为 Kotlin,需要直接指出 E 成员的真正type
1 |
|
Enum 的注释
在函数的注释中,加上 * @param xxx 实际E可能的值,例如
1 | /** |
Mapping
一个例子即可说完, 假设这里是 @package.xxx, 里面有
1 | declare type T = 'Ka' | 'g' | number |
-> Kotlin
1 |
|
Js Object
使用 Kotlin.js.json
注意:是 Json,不是全大写的那个
创建
1 | json( |
=
1 | { |
get/set
用这个,详情见源码注释
1 | public operator fun get(propertyName: String): Any? |
使用 Map (Kotlin 2.0+)
原理:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/fromEntries
1 | val jsonMap: MutableMap<String, dynamic> = mutableMapOf() |
其他
把鸿蒙的 .d.ts 快速换成 kotlin, 可以用下面的正则(不一定全),就是把 -> 左边的换成右边的,都是一直换到没有可换,然后顺序执行。没有箭头就是不用换的意思,以 VSCode 的正则表达式为例。
1 | [ ]*/\*.*\n([ ]*\*.*\n)*[ ]*/ -> / |
Kotlin 中生成的 external interface
external interface 是一个接口,它定义了一个类的成员,但不实现它们。如果你尝试实例化,里面的内容会被mangle掉,所以我们需要一系列 builder 来构造它。
1 | external interface Point { |
则同时需要一个 builder,例如使用 jso
1 | inline fun <T : Any> jso(block: T.() -> Unit): T { |
用起来就是
1 | val point = jso { |
小坑
Long
总所周知, JS 没有 64 bit Integer 这个东西,所以 Kotlin/JS 创造了下面的东西
1 | class long { |
什么拼好 long https://kotlinlang.org/docs/js-to-kotlin-interop.html#kotlin-types-in-javascript
但就出现了一个问题,这个东西并没有被 export,所以在 .d.ts 中,看到的是
1 | const x: any /* Long */ |
那传入 number,会因为缺少方法而爆炸(尤其是有人不知道这个特性)
不要去翻译的类
有一些类一定要直接换为 dynamic
LongArrayBuffer
还有所有不在这个文件中出现的类(import进来的类)
其他
- 不要用
sealed class表示enum,要直接用其实际值的类型 - 不要用全局的
@file:JsModule,在每个interface,object上加上@JsModule - 第一行加上
@file:Suppress("unused")
For AI:
Kotlin/JS 互操作编写规范
1. 顶层配置与模块声明 (Top-level & Module Declarations)
- 文件级抑制:在文件第一行添加
@file:Suppress("unused")避免不必要的 IDE 警告。 - 模块化声明:不要使用全局的
@file:JsModule。应该为每个具体的外部接口 (External Interface) 或外部对象 (External Object) 单独添加@JsModule("YourModule")注解。 - 真实的模块代码:
@JsModule绑定的模块内部必须有实际的代码声明(例如declare namespace {...})。如果目标 TS 模块仅仅是重导出 (Re-export,如import {xx}; export {xx}),在 Kotlin 中可能会报找不到 Module 的错误。
2. 类型映射规则 (Type Mappings)
枚举 (Enum)
- 禁用 Sealed Class:绝对不要使用
sealed class来模拟枚举。 - 降级为底层类型:直接将枚举映射为其在 JS 中实际的底层类型(通常是
Number或String)。 - 注释约束:使用 KDoc 注释 (KDoc Comments) 的
@param标签来标明实际允许的值域。
1 | /** |
复杂类型与函数 (Complex Types & Functions)
- 对于 TS 中的联合类型 (Union Types) 或复杂的类型体操,Kotlin 中直接使用
dynamic类型处理。 - 函数回调中的参数映射保持一致。
1 | // TypeScript 原型 |
3. 避免翻译的黑名单 (Blacklist for Translation)
遇到以下类型时,不要尝试在 Kotlin 中声明或翻译,请直接将其类型声明为 dynamic:
Long(64位整型):JS 原生不支持 64-bit Integer。Kotlin/JS 为了支持Long,在底层将其实现为一个包含high和low属性的对象。由于这个内部对象并未对外导出 (Exported),如果.d.ts中标注为any /* Long */,你直接在 Kotlin 传Number会因为缺少方法而崩溃。ArrayBuffer。- 外部导入类 (Imported Classes):所有不在当前正在翻译的
.d.ts文件内声明的类,直接视为dynamic。
4. JS 对象的创建与处理 (JS Object Manipulation)
方案 A:外部接口与构建器 (External Interface & Builder) - 推荐
如果直接实例化 external interface,内部属性名可能会被编译器混淆 (Name Mangling)。需要定义一个内联构建器 (Inline Builder) 来安全初始化:
1 | external interface Point { |
方案 B:使用原生 JSON (kotlin.js.json)
适用于动态生成简单的嵌套 JSON 结构,可通过 get / set 操作符直接读写属性:
1 | val obj = json( |
方案 C:使用 Map 转换 (Kotlin 2.0+)
利用 JS 全局对象 Object.fromEntries 实现 Map 到 JS 对象的转换:
1 | val jsonMap: MutableMap<String, dynamic> = mutableMapOf() |
5. 附录:.d.ts 快速正则转换 (Regex Replacement Toolkit)
如果需要将类似鸿蒙环境的 .d.ts 快速替换为 Kotlin 语法,可使用以下正则表达式 (Regular Expressions) 在 VSCode 等编辑器中进行顺序替换(一直替换到无匹配项为止):
| 匹配目标 (Find) | 替换为 (Replace) | 作用 |
|---|---|---|
[ ]*/\*.*\n([ ]*\*.*\n)*[ ]*/ |
/ |
清理多行注释区块 |
^[ ]*\* [ @<].*\n |
(空) | 清理特定注释标签行 |
;$ |
(空) | 移除行尾分号 |
:[ ]*number |
: Number |
转换数字类型 |
^[ ]*([a-zA-Z]*): |
val $1: |
将属性声明转为不可变变量 (val) |
^[ ]*([a-zA-Z]*)\( |
fun $1( |
将方法声明转为函数 (fun) |
(注意:正则替换仅用于起草,替换后仍需手动校对类型和保留字)
有什么具体场景在转换 TS 的时候卡住了吗?需要的话我可以帮你看看。
