跳转至正文
移动端 Capacitor

用于 Capacitor 和 Capgo 的 TypeScript API 示例

探索 Capacitor 插件与 Capgo 更新的 TypeScript API 实用示例。掌握类型化接口、监听器模式及实现策略。

用于 Capacitor 和 Capgo 的 TypeScript API 示例

每一个稳健的 TypeScript API 示例 Capacitor 的构建方式如出一辙:从类型化的插件接口开始。明确定义你的方法、选项和 Promise 返回值,这样你的 Web code 层与原生层就能共享一套由 TypeScript 强制执行的契约。

目录

构建强类型的 Capacitor 插件接口

该接口定义了 Web 端 code 所能看到的 API。底层的 Native Build 实现必须遵循该契约 —— TypeScript 会在应用运行前检查方法名、参数和返回值。

import { registerPlugin } from ‘@capacitor/core’;

export interface DeviceStatus {\nonline: boolean;\nbatteryLevel?: number;\n}

export interface DevicePlugin {\ngetStatus(): Promise}; setLabel(options: { label: string }): Promise<{ saved: boolean }> }

export const Device = registerPlugin(‘Device’);

这几行代码包含了很多关键点:

  • 显式返回类型 确保每个结果都可预测。
  • 类型化的 options 对象 在编译时捕获缺失或拼写错误的属性。
  • 基于 Promise 的方法 模拟异步完成的原生工作。
  • 泛型 registerPlugin 调用 是将 Web API 连接到原生桥接层的纽带。
  • 接口 (Interfaces) 用于定义契约,且不会增加任何运行时 code。

每个调用点都享有相同的待遇:

const status = await Device.getStatus(); console.log(status.online);

await Device.setLabel({ label: ‘Production’ });

切换 { label: 'Production' } 以查找 { name: 'Production' } 编译器会立即报错。这总比移动端发布后才发现不匹配要好。

Interface 也是建模可选值和失败场景的地方。如果一个 native 方法无法始终返回电池电量数据, batteryLevel?: number 提醒所有调用方进行处理 undefined.

下图展示了类型化方法、选项、返回值、bridge 定义以及编译时检查是如何在 Capacitor API 中协同工作的。

该图展示了为移动端开发框架提供强类型 TypeScript 插件接口的核心优势。

核心理念: 类型定义从面向 Web 的接口流向 native 平台逻辑,并且编译时检查会在每个调用点进行守卫。

API 设计快速查阅

要素 用途 示例
方法签名 定义可调用行为 getStatus()
Options 类型 控制输入结构 { label: string }
Promise 结果 表示异步任务 Promise<DeviceStatus>
Result 接口 定义返回数据 online: boolean

如需深入了解,请阅读 使用 TypeScript 构建 API 的指南。有两个习惯值得保持:不要在客户端 code 中泄露密钥和签名凭据,并在发布前针对各平台实现测试接口。

移动端团队需要同时处理 JavaScript、原生 code、设备权限以及各种异步平台服务。一个 强大的 TypeScript 契约 在这些边界处就像一份共享清单,确保在任何 code 推送到 iOS 或 Android 设备之前,所有预期都已明确。

一个人正在笔记本电脑上写代码,桌上放着咖啡杯,屏幕上显示着 code。

为了展示一个具体的 TypeScript API 示例,请对比一个返回 Promise<DeviceStatus> 与一个返回未定义类型(untyped)数据的函数。类型化版本能准确告知编辑器和每位代码审查者存在哪些字段;而未定义类型版本则会将这种发现成本转嫁给运行时日志、手动测试,甚至最糟糕的情况——线上事故。

采用趋势

TypeScript 已不再局限于前端领域。其采用率已攀升至 2024 年的 35% 开发者,远高于此前仅有的 2017 年为 12%,并且超过 一百万 GitHub 贡献者 在 2025 年将其列为首选语言。如果你想查看 TypeScript 普及率调查详情 请深入研究原始数据。

这一趋势对移动端团队具有实际意义。招聘、onboarding 以及 code 代码审查正日益围绕共享类型展开。新加入 Capacitor 项目的人员只需阅读 interface,即可理解预期的 native 行为,无需追踪每一处实现细节。

类型化的 API 也让发布工作更易于推导。当某个方法要求特定的 options 对象时,属性重命名或字段缺失会在编译时报错,而不是静默地产生一个不完整的 native 请求。

强类型将关键反馈提前,让修复只需几分钟,而非紧急发布 hotfix。

Capacitor 团队的优势

跨平台应用通常在多个 native 实现之上暴露一个面向 Web 的 API。虽然 TypeScript 无法证明每个 native 细节的行为完全一致,但它可以确保你在整个应用中的调用保持一致。

为以下内容应用显式类型:

  • 方法输入参数,包括必填和可选配置项
  • Promise 返回结果,确保成功返回的数据结构始终可预测
  • 事件与监听器,使回调函数能够处理已知的 Payload
  • 错误与状态值,从而保证回退路径清晰可见

这种结构在集成设备插件或业务服务时优势显著。在审查更新自动化流程时,它也能为团队提供帮助,因为错误的 channel、bundle identifier 或兼容性字段可能会对大量用户产生连锁影响。

深入了解相关模式,请阅读 我们关于使用 OpenAPI 生成类型化 API 的指南. 它介绍了如何通过共享定义来减少 API 文档与应用 code 之间常见的同步偏差。

论证业务价值

严格类型化需要一定的初期投入,尤其是当旧的 JavaScript code 包含不一致的数据结构时。但长期来看回报显著:重构工作量更小、职责更清晰,且集成时的意外情况会大幅减少。

从风险最高的边界开始:

  1. 为原生和远程调用定义响应接口。
  2. 为配置对象和事件 Payload 添加类型。
  3. 逐步开启严格的编译器检查。
  4. 在发布任何 update 之前强制进行类型检查。

对于企业级移动端团队,这一基础能确保在不同平台、版本和贡献者之间保持可预测的维护成本。

Capacitor 的 ScreenOrientationPlugin 是一个极佳的 TypeScript API 示例 因为它将少量简单的 Web 方法映射到了特定平台的设备行为上。公共契约在各平台保持一致,而 iOS 和 Android 则在底层各自处理原生的细节。

import { registerPlugin } from ‘@capacitor/core’;

export type OrientationType =\n| ‘portrait-primary’\n| ‘portrait-secondary’\n| ‘landscape-primary’\n| ‘landscape-secondary’;

export interface OrientationData {\ntype: OrientationType;\nangle: number;\n}

export interface LockOptions { orientation: OrientationType; }

export interface ScreenOrientationPlugin { orientation(): Promise};\nlock(options: LockOptions): Promise;\nunlock(): Promise;\naddListener(\neventName: ‘screenOrientationChange’,\nlistenerFunc: (data: OrientationData) =&gt; void,\n): Promise&lt;{ remove: () =&gt; Promise }&gt; }

export const ScreenOrientation =\nregisterPlugin(‘ScreenOrientation’);

以下是各签名的快速参考:

  • orientation() — 异步读取当前方向
  • lock() — 仅接受已知的方向值
  • unlock() — 将控制权交还给设备默认行为
  • addListener() — 方向变化时触发带类型的 payload

由于每个方法都返回 Promise,你可以对原生 bridge 和浏览器实现使用相同的调用模式。无需分支判断,无需处理特殊情况。

const current = await ScreenOrientation.orientation();

if (current.type.startsWith(‘landscape’)) {\nconsole.log("}Angle: ${current.angle}); }

await ScreenOrientation.lock({ orientation: 'landscape-primary', });

输入一个拼写错误的值,例如 landscape-main 构建会立即失败。这是一个几秒钟就能修复的编译错误,而不是需要通过设备 logs 苦苦追踪的平台特定运行时 bug。

正确编写 Listener 参数

Listener 应当像普通方法一样严谨。避免 any 在这里,因为它隐藏了事件 payload 与返回结果之间的差异 orientation() 结果。

const handleChange = (data: OrientationData) =&gt; {\ndocument.body.dataset.orientation = data.type;\n};

const subscription = await ScreenOrientation.addListener( ‘screenOrientationChange’,\nhandleChange,\n);

await subscription.remove();

仅在两个原生实现都保证具有相同字段时 OrientationData 才共享单一 type。如果其中一个平台省略了 angle,请将其标记为可选,并强制调用方处理 undefined.

设计选择 更安全的模式
输入 具名 options 接口
结果 显式 Promise 类型
活动 字面量事件名称
清理 返回可移除的订阅

接口是契约桥梁,而非原生实现。 保持接口精简、可预测且易于测试。

有关平台行为、权限和安装步骤,请阅读 Capacitor Screen Orientation 插件指南。最后一个值得养成的好习惯:在严格的 TypeScript 设置下,同时测试有效调用和被拒绝的调用。这种组合能在你打包移动应用之前,就提前发现错误的方法名、缺失的字段以及不兼容的监听器 payload。

Capgo 为 Capacitor 团队提供了一种无需等待 App Store 审核即可推送 JavaScript、CSS、配置和资源修复的方法。诀窍在于将它的更新 pipeline 视为任何其他类型化的 API 边界,从而确保在 bundle 到达用户设备之前,频道、发布规则、兼容性检查和回滚决策都是明确且显式的。

一个人拿着智能手机,展示了纵向和横向屏幕模式的区别。

定义 Update Contracts

首先要明确你的自动化流程接受哪些具体值。使用字面量联合类型(Literal unions)可以防止误部署到错误的 channel,而 interface 则能让 bundle 与其所需的 native version 之间的关系实现自文档化。

type Channel = ‘beta’ | ‘staging’ | ‘production’;

interface UpdateRequest {\nchannel: Channel;\nbundleVersion: string;\nminNativeVersion: string;\nrolloutPercent: number;\nsigned: boolean;\n}

interface UpdateResult {\naccepted: boolean;\nappliedOnNextLaunch: boolean;\nrollbackEnabled: boolean;\n}

一个简洁的 TypeScript API 示例 在将请求交给 Capgo client 之前进行校验:

async function publishUpdate(\nrequest: UpdateRequest,\n): Promise { if (!request.signed || request.rolloutPercent < 0 || request.rolloutPercent > 100) { throw new Error(‘Unsafe update request’); }

return capgo.publish(request);\n}

Capgo SDK 版本之间的客户端方法名可能会发生变化,因此请将其封装在自己的 interface 中。这种隔离能让你在每次升级时都游刃有余,并防止特定厂商的实现细节渗透到你的整个代码库中。

保护 Channel 与兼容性

选择 Channel 时不应草率。生产环境发布需要比 Beta 测试更严格的检查,特别是当 Web bundle 调用了早期 App 版本中尚不存在的 Native 能力时。

function canDeploy( request: UpdateRequest, installedNativeVersion: string, ): boolean { return request.signed && installedNativeVersion >= request.minNativeVersion; }

不要直接用字符串比较版本号。引入一个专业的 semantic-version 库,以便 1.10.0 排序后 1.9.0。在任何内容发布到 Channel 之前,请运行以下检查清单:

  1. 确认 bundle 已签名。
  2. 验证目标 Channel 是否符合预期。
  3. 对比 Native 与 bundle 的兼容性范围。
  4. 先向小范围用户发布。
  5. 监控失败信号并随时准备回滚。

类型化的更新流水线将发布策略转化为 code,方便审核人员和 CI 进行实际检查。

Capgo 的增量交付和 Channel 控制能完美契合这一模式,其设备级可观测性还能让团队在事后追踪采用情况或失败信号。关于事件埋点方面,请参阅 使用 Capgo 进行自定义事件追踪指南.

签名密钥和管理员凭据应保存在服务器或 CI 系统中,而非分发的 App 内。在下次启动时应用更新,通过故意拒绝 Bundle 来测试回滚,并使用类型化的结果记录每一次决策。这种组合既能保证快速交付,又能兼顾严谨的移动端发布控制。

类型化的监听器是让异步 API 值得信赖的关键。无论是追踪屏幕方向的回调,还是 Capgo 的更新事件,在所有平台上都应接收相同的 Payload 结构 —— 并且应当由编译器来强制执行这一点。

interface UpdateEvent {\nversion: string;\nchannel: ‘beta’ | ‘production’;\navailable: boolean;\n}

类型 Listener = (payload: T) => void;

interface UpdateService {\naddListener(\nevent: ‘updateAvailable’,\ncallback: Listener, ): Promise<{ remove: () => Promise }> removeAllListeners(): Promise; }

这个 TypeScript API 示例 将事件名称固定为字面量,并将回调函数与类型化的 payload 绑定。你的编辑器可以自动补全 version 代码,且编译器会拒绝任何预期无关数据的回调。虽然只需少量配置,但每次 API 变更时都能带来回报。

想看实际效果?观看演示视频:

安全地注册监听器

在组件内部,保留订阅句柄以确保清理过程显式化。同样的模式可以无缝应用于 Angular 生命周期钩子、React effects 和 Vue mount 钩子,无需任何改动。

let orientationHandle: { remove: () => Promise } | undefined;

async function start() { orientationHandle = await ScreenOrientation.addListener( ‘screenOrientationChange’, ({ type, angle }) => { console.log(type, angle); }, ) }

async function stop() {\nawait orientationHandle?.remove();\norientationHandle = undefined;\n}

在页面消失时执行清理工作,而不仅仅是在整个 App 关闭时。如果忽略这一点,页面跳转会导致回调仍挂载在原生事件源上,最终导致重复执行和难以追踪的陈旧状态更新。

每个框架都为此提供了相应的钩子:

  • Angular — 从...触发清理 ngOnDestroy
  • React — 从...返回一个异步安全的清理函数 useEffect
  • Vue — 在...中移除订阅 onBeforeUnmount

每一个 addListener 调用都应该有对应的移除路径。

选择正确的清理方法

当一个组件拥有单个订阅时,返回 handle 是最佳选择。 removeAllListeners() 当一个 service 持有多个监听器并需要完全重置时,这种方式表现出色。

async function resetUpdates(service: UpdateService) {\nawait service.removeAllListeners();\n}

当其他页面仍依赖该服务时,不要在共享组件中触发全局方法。如果所有权是局部的,请坚持使用独立的 remove() 句柄。

场景 建议操作
单个组件订阅 调用 handle.remove()
服务关闭 调用 removeAllListeners()
重复注册 守卫初始化
未知 Payload 使用前进行校验

对于 Capgo 通知,请将更新 payload 与设备事件分开处理。随后分别测试注册、送达和清理流程 —— Capgo 自定义事件追踪指南 在集成方面提供了更多细节。

发布前,请检查三件事:卸载时是否确实移除了 handler、rejected promises 是否已被捕获,以及是否有 callback 尝试更新已销毁的组件。这种严谨性可以确保基于 Angular、React 和 Vue 的响应式 Capacitor 应用始终保持流畅。

一名专业的软件开发人员正戴着耳机,在双显示器配置下进行 code 开发。

一个构建良好的 TypeScript API 示例 应从能够体现意图的命名开始。方法使用动词,接口使用名词,并坚持使用一致的后缀,例如 Options, Result,以及 Event。清晰的命名可以缩短 onboarding 时间,因为开发者无需查看实现代码即可理解其契约。

保持公共接口精简。通过专注的方法来暴露功能,而不是将松散相关的操作全部堆砌到一个对象中。

  • getStatus() 读取状态。
  • updateConfig(options) 配置变更。
  • addListener(event, callback) 订阅变更。

精确定义输入与输出类型

当参数可能增加时,建议使用具名选项接口:

interface PublishOptions {\nchannel: ‘beta’ | ‘production’;\nrolloutPercent: number;\n}

interface PublishResult {\nversion: string;\naccepted: boolean;\n}

async function publish(\noptions: PublishOptions,\n): Promise { return client.publish(options); }

当 API 包装不同 payload 但需要保留其特定类型时,泛型就派上用场了:

interface ApiResponse { data: T;\nrequestId: string;\n}

async function request(path: string): Promise<ApiResponse>> { return fetchJson<ApiResponse>>(path); }

不要为了追求灵活性而盲目添加泛型。泛型应当表达输入与输出之间的真实关系——否则,使用具体的 interface 会更易于阅读和维护。

让无效状态难以被表示,尤其是在 native、网络和 update 的边界处。

在契约(contract)旁边记录行为。涵盖权限、单位、rejected promises、可选字段,以及方法是立即生效还是在下次启动时生效。行内注释应解释决策原因,而不是简单重复方法名。

为 Change 组织 Code

将类型、客户端逻辑、平台适配器和测试分离到可预测的文件中。从单一入口点导出公共类型,并将实现细节保持私有。

关注点 推荐位置
公共接口 types.ts
API 方法 client.ts
Native 适配器 platform/
兼容性测试 tests/

针对破坏性的插件变更,应引入新的主接口或兼容层,暂时保留已弃用的方法,并编写迁移指南。 了解更多关于 API 的版本策略 在更改调用方之前。

在发布前,务必在 CI 中运行严格的类型检查和契约测试。对于 Capgo 工作流,应将通道值、Native 兼容性、已签名 bundle 以及回滚行为验证为类型化的发布规则 —— 这能确保随着团队、平台和集成规模的扩大,快速更新依然处于受控状态。

如何为动态 Native 结果定义类型?

不要让 any 当 Native 方法返回不可预测的数据时,不要让其泄露到你的 code 中。相反,应明确定义你可以确定的字段,并使用 ?将真正可选的值标记出来,并在边界处对不确定的输入进行清洗,确保下游逻辑不会受到影响。

interface NativeResult {\nsuccess: boolean;\nvalue?: string;\n}

async function readValue(): Promise { const result = await NativePlugin.read(); return { success: Boolean(result.success), value: typeof result.value === 'string' ? result.value : undefined, }; }

这种方法在确保调用方安全的同时,通过类型本身明确了不确定性。若想更深入了解这种接口模式,请回顾 使用 TypeScript 构建 API.

监听器如何避免未处理的 Rejection?

异步回调在设计上必须具备防御性。应当在监听器内部捕获失败,而不是寄希望于事件系统能静默处理 rejected promises。

const handleUpdate = (event: UpdateEvent): void => { void applyUpdate(event).catch((error: unknown) => { console.error(‘Update failed’, error); }); };

请保留订阅引用,并在组件卸载时将其移除。这可以防止重复回调和过时的状态更新 —— 监听器生命周期章节对此有详细说明。

每个异步监听器都需要错误处理路径和清理路径。

如何保护 Capgo 更新?

签名密钥和管理凭据应始终保留在您的服务器或 CI 系统中。仅此而已。客户端应仅接收已签名的 bundle 并使用类型化结果来显示状态 —— 绝不能用于创建签名。

在发布之前,请设置独立的 channel union,运行兼容性检查,配置 rollout 限制,并规划好回滚路径。 Capgo 为 Capacitor 和 Electron 应用提供签名交付、渠道控制、下次启动应用及回滚保护。通过其文档可以了解如何利用类型化的发布工作流来强化你的 update pipeline。

为 Capacitor 应用提供实时更新

当 Web 层 Bug 已上线时,通过 Capgo 推送修复,不必等 App Store 审核数天。用户会在后台获取更新,而原生变更仍走常规审核流程。

博客最新文章

Capgo 为你提供打造真正专业移动应用所需的深度洞察。