Aller au contenu principal
Mobile Capacitor

Exemple TypeScript API pour Capacitor et Capgo

Explorez un exemple pratique d'API TypeScript pour les plugins Capacitor et les mises à jour Capgo. Maîtrisez les interfaces typées, les patterns de listener et les stratégies d'implémentation.

Exemple TypeScript API pour Capacitor et Capgo

Chaque exemple d'API TypeScript pour Capacitor commence de la même manière : avec une interface de plugin typée. Définissez explicitement vos méthodes, vos options et les résultats Promise, afin que votre code web et la couche native partagent un contrat unique que TypeScript impose réellement.

Table of Contents

Créez une interface de plugin Capacitor fortement typée

L'interface décrit l’API que votre code web voit. L'implémentation native sous-jacente doit respecter ce contrat — et TypeScript vérifie les noms de méthodes, les paramètres et les valeurs de retour avant même l'exécution de votre app.

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’);

Beaucoup de choses se passent dans ces quelques lignes :

  • Types de retour explicites rendent chaque résultat prévisible.
  • Objets d'options typés catch missing or misspelled properties at compile time.
  • Méthodes basées sur les Promise modéliser le travail natif qui s'exécute de manière asynchrone.
  • Un générique registerPlugin appel est ce qui connecte l’API web au bridge natif.
  • Interfaces documentent le contrat sans ajouter un seul octet de code au runtime.

Chaque point d'appel reçoit le même traitement :

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

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

Remplacer { label: 'Production' } dans les { name: 'Production' } et le compilateur le signale immédiatement. Cela vaut mieux que de découvrir l'incohérence après une release mobile.

L'interface est aussi l'endroit où vous modélisez les valeurs optionnelles et les cas d'erreur. Si une méthode native ne peut pas toujours fournir une lecture de batterie, batteryLevel?: number indique à chaque appelant de gérer undefined.

Le diagramme ci-dessous montre comment les méthodes typées, les options, les valeurs de retour, les définitions de bridge et les vérifications à la compilation s'articulent au sein d'un Capacitor API.

Un diagramme illustrant les principaux avantages d'une interface de plugin TypeScript fortement typée pour les frameworks de développement mobile.

L'idée centrale : les définitions de type circulent de l'interface web vers la logique de la plateforme native, et les vérifications à la compilation sécurisent chaque point d'appel.

Aperçu rapide du design API

Élément Finalité Exemple
Signature de la méthode Définit le comportement appelable getStatus()
Type des options Contrôle la structure de l'entrée { label: string }
Résultat de la Promise Représente un travail asynchrone Promise<DeviceStatus>
Interface du résultat Définit les données retournées online: boolean

Pour une référence plus approfondie, lisez le guide sur la création d'API en TypeScript. Deux habitudes à adopter : ne laissez pas de secrets ou d'identifiants de signature dans le code client, et testez l'interface par rapport à chaque implémentation de plateforme avant de publier.

Les équipes mobiles jonglent simultanément avec le JavaScript, le code natif, les permissions de l'appareil et les services de plateforme asynchrones. Un contrat TypeScript robuste agit comme une checklist partagée à chaque interface, rendant les attentes explicites avant qu'un code n'atterrisse sur un appareil iOS ou Android.

Une personne codant sur un ordinateur portable avec code affiché sur un bureau à côté d'un mug de café.

Pour un exemple concret exemple de API TypeScriptconcret, comparez une méthode qui retourne Promise<DeviceStatus> avec une autre qui renvoie des données non typées. La version typée indique à votre éditeur et à chaque relecteur exactement quels champs existent. La version non typée reporte ce travail de découverte sur les logs au runtime, les tests manuels et, au pire, sur des incidents en production.

Signaux d'adoption

TypeScript a largement dépassé sa niche front-end. Son adoption a grimpé à 35 % des développeurs en 2024, contre seulement 12 % en 2017, et plus d' un million de contributeurs GitHub l'ont citée comme langage principal d'ici 2025. Explorez les résultats complets de l' adoption de TypeScript si vous voulez les chiffres bruts.

Cette trajectoire a un impact concret pour les organisations mobiles. Le recrutement, l'onboarding et la revue de code s'articulent de plus en plus autour de types partagés. Un développeur rejoignant un projet Capacitor peut lire une interface et comprendre le comportement natif attendu sans avoir à tracer chaque implémentation.

Les API typées facilitent aussi la compréhension des releases. Lorsqu'une méthode exige un objet d'options spécifique, une propriété renommée ou un champ manquant échouera au temps de build plutôt que de générer silencieusement une requête native incomplète.

Le typage fort permet de détecter les erreurs plus tôt, quand un correctif prend quelques minutes au lieu d'une release de hotfix en urgence.

Avantages pour les équipes Capacitor

Les apps cross-platform exposent généralement un API web qui repose sur plusieurs implémentations natives. TypeScript ne peut pas prouver que chaque détail natif se comporte de manière identique, mais il permet de garder vos appels cohérents dans toute l'application.

Appliquez des types explicites aux :

  • Entrées de méthode, incluant les options requises et optionnelles
  • Résultats de Promise, pour que les données de succès aient toujours une structure prévisible
  • Événements et listeners, pour que les callbacks gèrent des payloads connus
  • Erreurs et valeurs de statut, pour que les chemins de repli restent visibles

Cette structure est payante lors de l'intégration de plugins device ou de services opérationnels. Elle aide aussi les équipes qui révisent l'automatisation des mises à jour, où un mauvais canal, un bundle identifier ou un champ de compatibilité peut impacter une large base d'utilisateurs.

Pour approfondir ces patterns, lisez notre guide sur la génération d'API typées avec OpenAPI. Il explique comment les définitions partagées réduisent le décalage manuel qui s'installe normalement entre la documentation API et le code de l'application.

Justifier l'investissement

Le typage strict demande un investissement initial, surtout quand l'ancien code JavaScript contient des structures de données incohérentes. Le retour sur investissement se voit avec le temps : des refactors plus simples, une meilleure gestion des responsabilités et beaucoup moins de surprises lors de l'intégration.

Commencez par les limites qui présentent le plus de risques :

  1. Définissez des interfaces de réponse pour les appels natifs et distants.
  2. Typez vos objets d'options et vos payloads d'événements.
  3. Activez progressivement les vérifications strictes du compilateur.
  4. Exigez des vérifications de type avant de publier une mise à jour.

Pour les équipes mobiles en entreprise, cette base rend la maintenance prévisible sur toutes les plateformes, releases et contributeurs.

Capacitor’s ScreenOrientationPlugin est un excellent Exemple de API TypeScript car il mappe quelques méthodes web simples sur le comportement spécifique de l'appareil selon la plateforme. Le contrat public reste identique sur toutes les plateformes, tandis qu'iOS et Android gèrent chacun leurs propres détails natifs en arrière-plan.

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 {\norientation: OrientationType;\n}

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’);

Voici un aide-mémoire pour chaque signature :

  • orientation() — lit l'orientation actuelle de manière asynchrone
  • lock() — n'accepte que des valeurs d'orientation connues
  • unlock() — redonne le contrôl’au comportement normal de l'appareil
  • addListener() — déclenche un payload typé à chaque changement d'orientation

Comme chaque méthode retourne une Promise, vous pouvez utiliser le même pattern d'appel pour le bridge natif et pour une implémentation navigateur. Pas de branchements, pas de cas particuliers.

const current = await ScreenOrientation.orientation();

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

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

Tapez une valeur mal orthographiée comme landscape-main et le build échoue instantanément. C'est une erreur de compilation que vous corrigez en quelques secondes — pas un bug runtime spécifique à une plateforme que vous devez traquer dans les logs de l'appareil.

Tapez correctement les arguments du Listener

Les Listeners méritent la même rigueur que les méthodes classiques. Évitez any ici, car cela masque la différence entre le payload d'un événement et le résultat orientation() renvoyé.

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

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

await subscription.remove();

Ne partagez qu'un seul OrientationData type lorsque les deux implémentations natives garantissent les mêmes champs. Si une plateforme omet angle, marquez-le comme optionnel et forcez les appelants à gérer undefined.

Choix de conception Pattern plus sûr
Entrées Interfaces d'options nommées
Résultats Types de Promise explicites
Événements Noms d'événements littéraux
Nettoyage Retourner un abonnement supprimable

L'interface est le contrat de liaison, pas l'implémentation native. Gardez-la petite, prévisible et testable.

Pour le comportement de la plateforme, les permissions et les étapes d'installation, lisez le guide du plugin Capacitor Screen Orientation. Une dernière habitude à adopter : testez les appels valides et les appels rejetés avec des paramètres TypeScript stricts. Cette combinaison permet de détecter les noms de méthodes erronés, les champs manquants et les payloads de listener incompatibles bien avant de packager votre application mobile.

Capgo permet aux équipes de Capacitor de déployer des correctifs JavaScript, CSS, de configuration et d'assets sans subir la revue des stores. L'astuce consiste à traiter son pipeline de mise à jour comme n'importe quelle autre limite API typée, afin que les canaux, les règles de déploiement progressif, les vérifications de compatibilité et les décisions de rollback restent explicites avant qu'un bundle n'atteigne l'appareil d'un utilisateur.

Une personne tenant un smartphone montrant la différence entre les modes d'orientation portrait et paysage.

Définir les contrats de mise à jour

Commencez par définir précisément les valeurs acceptées par votre automatisation. Les unions littérales vous empêchent de déployer par erreur sur le mauvais canal, et les interfaces rendent la relation entre un bundle et sa version native requise auto-documentée.

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}

Un exemple simple Exemple TypeScript API valide la requête avant de la transmettre au client Capgo :

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}

Le nom exact de la méthode client change entre les versions de Capgo SDK, alors encapsulez-la derrière votre propre interface. Cette isolation est payante à chaque mise à jour et empêche les détails spécifiques au fournisseur de se propager dans votre codebase.

Sécuriser les canaux et la compatibilité

Le choix d'un canal ne doit pas se faire au hasard. Une release en production exige des vérifications plus strictes qu'une expérimentation bêta, surtout quand le bundle JS appelle des capacités natives qui n'existaient pas dans les versions précédentes de l'app.

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

Ne comparez pas les versions avec de simples chaînes de caractères. Utilisez une bibliothèque de versionnage sémantique pour 1.10.0 trie après 1.9.0. Avant d'envoyer quoi que ce soit sur un canal, passez par une checklist :

  1. Confirmez que le bundle est signé.
  2. Vérifiez que le canal cible correspond à l'intention.
  3. Comparez les plages de compatibilité entre le natif et le bundle.
  4. Publiez d'abord auprès d'une audience limitée.
  5. Surveillez les signaux d'erreur et gardez un rollback prêt.

Un pipeline de mise à jour typé transforme la politique de release en code que les relecteurs et la CI peuvent réellement inspecter.

La livraison différentielle et les contrôles de canaux de Capgo s'intègrent parfaitement à ce modèle, et son observabilité au niveau de l'appareil permet aux équipes de retracer l'adoption ou les signaux d'échec après coup. Pour l'instrumentation des événements, consultez ce guide sur le suivi d'événements personnalisés avec Capgo.

Les clés de signature et les identifiants admin doivent rester sur le serveur ou le système de CI, pas dans l'app livrée. Appliquez la mise à jour au prochain lancement, testez le rollback avec un bundle intentionnellement rejeté, et loguez chaque décision avec un résultat typé. Cette combinaison permet de concilier livraison rapide et contrôle rigoureux des releases mobiles.

Ce sont les listeners typés qui rendent les API asynchrones dignes de confiance. Qu'un callback suive l'orientation de l'écran ou un événement de mise à jour Capgo, il doit recevoir la même structure de payload sur chaque plateforme — et c'est au compilateur de l'imposer.

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

type Listener = (payload: T) =&gt; void;

interface UpdateService {\naddListener(\nevent: ‘updateAvailable’,\ncallback: Listener"}, ): Promise<{ remove: () =&gt; Promise }&gt;;\nremoveAllListeners(): Promise; }

Ceci Exemple TypeScript API fixe le nom de l'événement à un littéral et associe le callback à un payload typé. Votre éditeur propose l'autocomplétion version gratuitement, et le compilateur rejette tout callback qui s'attend à des données sans rapport. C'est un peu de configuration, et ça vaut le coup à chaque fois que API change.

Vous préférez le voir en action ? Regardez le tutoriel :

Enregistrer les listeners en toute sécurité

À l'intérieur d'un composant, conservez la référence de l'abonnement pour que le nettoyage reste explicite. Ce même pattern s'adapte aux hooks de cycle de vie Angular, aux effets React et aux hooks de montage Vue sans modification.

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}

Exécutez le nettoyage lorsqu'un écran disparaît — pas seulement quand l'application entière se ferme. Si vous l'oubliez, la navigation laisse des callbacks attachés aux sources d'événements natifs. Vous vous retrouvez avec des tâches dupliquées et des mises à jour d'état obsolètes pénibles à tracer.

Chaque framework propose un hook pour cela :

  • Angular — déclencher le nettoyage depuis ngOnDestroy
  • React — retourner une fonction de nettoyage async-safe depuis useEffect
  • Vue — supprimer l'abonnement dans onBeforeUnmount

Chaque addListener l'appel doit avoir un chemin de suppression correspondant.

Choisir la bonne méthode de nettoyage

Un handle retourné est le bon choix quand un composant possède un seul abonnement. removeAllListeners() est idéale quand un service gère plusieurs écouteurs et est entièrement réinitialisé.

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

N'appelez pas la méthode globale depuis un composant partagé si d'autres écrans dépendent encore du service. Si la propriété est locale, privilégiez des remove() handles.

Situation Action recommandée
Abonnement par composant Appelez handle.remove()
Arrêt du service Appelez removeAllListeners()
Enregistrement répété Initialisation de la garde
Payload inconnu Valider avant usage

Pour les notifications Capgo, séparez les payloads de mise à jour des événements de l'appareil. Testez ensuite l'enregistrement, la livraison et le nettoyage séparément — le Guide de suivi des événements personnalisés Capgo en dit plus côté intégration.

Avant de livrer, vérifiez trois points : le démontage supprime bien les handlers, les promesses rejetées sont capturées, et aucun callback ne peut mettre à jour un composant détruit. Cette discipline garde les apps Capacitor réactives avec Angular, React et Vue.

Un développeur logiciel professionnel travaillant sur code avec un double écran et un casque audio.

Un code bien structuré Exemple TypeScript API commence par des noms qui décrivent l'intention. Utilisez des verbes pour les méthodes, des noms pour les interfaces, et gardez des suffixes cohérents comme Options, Result, et Event. Des noms clairs réduisent le temps d'onboarding car les développeurs comprennent le contrat sans ouvrir l'implémentation.

Gardez les interfaces publiques légères. Exposez les capacités via des méthodes ciblées plutôt que d'entasser des opérations peu liées sur un seul objet.

  • getStatus() lit l'état.
  • updateConfig(options) modifie la configuration.
  • addListener(event, callback) s'abonne aux changements.

Typage précis des entrées et sorties

Utilisez des interfaces d'options nommées si les paramètres risquent d'évoluer :

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

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

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

Les génériques sont indispensables lorsqu'un API enveloppe différents payloads tout en devant préserver leurs types spécifiques :

interface ApiResponse { data: T; requestId: string; }

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

Mais n'ajoutez pas de génériques juste pour paraître flexible. Un générique doit exprimer une relation réelle entre l'entrée et la sortie — sinon, une interface concrète est plus facile à lire et à maintenir.

Rendez les états invalides difficiles à représenter, surtout aux limites du natif, du réseau et des mises à jour.

Documentez le comportement juste à côté du contrat. Couvrez les permissions, les unités, les promesses rejetées, les champs optionnels, et si une méthode s'applique immédiatement ou au prochain lancement. Les commentaires inline doivent expliquer les décisions, pas répéter le nom des méthodes.

Organiser Code pour Change

Séparez les types, la logique client, les adaptateurs de plateforme et les tests dans des fichiers prévisibles. Exportez les types publics depuis un point d'entrée unique et gardez les détails d'implémentation privés.

Préoccupation Emplacement recommandé
Interfaces publiques types.ts
méthodes API client.ts
Adaptateurs natifs platform/
Tests de compatibilité tests/

Pour les changements de plugin majeurs, introduisez une nouvelle interface majeure ou une couche de compatibilité, conservez temporairement les méthodes dépréciées et documentez les étapes de migration. En savoir plus sur les stratégies de versioning API avant de modifier les consommateurs.

Exécutez des vérifications de types strictes et des tests de contrat en CI avant de déployer. Pour les workflows Capgo, vérifiez les valeurs des canaux, la compatibilité native, les bundles signés et le comportement de rollback en tant que règles de release typées — cela permet de maîtriser les mises à jour rapides à mesure que les équipes, les plateformes et les intégrations grandissent.

Comment typer les résultats natifs dynamiques ?

Ne laissez pas any fuiter dans votre code lorsqu'une méthode native renvoie des données imprévisibles. Au lieu de cela, définissez explicitement les champs sur lesquels vous pouvez compter, marquez les valeurs réellement optionnelles avec ?, et assainissez les entrées incertaines à la limite avant qu'elles ne soient traitées par le reste du système.

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

async function readValue(): Promise {\nconst result = await NativePlugin.read();\nreturn {\nsuccess: Boolean(result.success),\nvalue: typeof result.value === ‘string’ ? result.value : undefined,\n};\n}

Cette approche protège vos appelants tout en rendant l'incertitude explicite dans le type lui-même. Pour une vue d'ensemble de ce pattern d'interface, consultez la création d'API en TypeScript.

How Can Listeners Avoid Unhandled Rejections?

Les callbacks asynchrones doivent être défensifs par conception. Capturez les échecs à l'intérieur du listener lui-même plutôt que de faire confiance au système d'événements pour ignorer silencieusement les promesses rejetées.

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

Conservez la référence de l'abonnement et supprimez-la lors du démontage du composant. Cela évite les doublons de callbacks et les mises à jour d'état obsolètes — la section sur le cycle de vie des listeners détaille ce point.

Chaque listener asynchrone nécessite à la fois un chemin d'erreur et un chemin de nettoyage.

Comment protéger les mises à jour Capgo ?

Les clés de signature et les identifiants administratifs restent sur votre serveur ou votre système CI. Point final. Le client ne doit recevoir que des bundles signés et utiliser des résultats typés pour afficher le statut — jamais pour créer des signatures.

Avant la publication, configurez des unions de canaux distinctes, lancez des tests de compatibilité, définissez les limites de déploiement progressif et planifiez votre stratégie de rollback. Capgo gère la livraison signée, le contrôle des canaux, le lancement de l'application au prochain démarrage et la protection contre le rollback pour Capacitor et les apps Electron. Leur doc montre comment des workflows de release typés peuvent sécuriser votre pipeline de mise à jour.

Live updates pour les apps Capacitor

Quand un bug de la couche web est en production, déployez le correctif via Capgo au lieu d'attendre des jours l'approbation des stores. Les utilisateurs reçoivent la mise à jour en arrière-plan, tandis que les changements natifs suivent le circuit de validation habituel.

Les derniers articles du blog

Capgo vous apporte les meilleures informations pour créer une application mobile vraiment professionnelle.