😽

TypeScriptによるkintoneプラグイン開発に便利な2つの公式ライブラリの型定義

に公開

こんにちは。株式会社シータグCTOの @y_okady です。

シータグでは社内外向けのkintoneプラグインをTypeScriptで開発しています。TypeScriptによる開発では、ライブラリが公式提供している型定義を利用することで効率的に開発を進めることができます。kintoneの場合は @kintone/dts-gen がそれに当たります。

@kintone/dts-gen は特定のkintoneアプリの型定義を生成するツールで、TypeScriptでアプリをカスタマイズする際に効果を発揮します。しかし、プラグインの開発、特にプラグイン設定画面の開発に必要な型定義が十分に備わっているわけではありません。一方、JavaScript/TypeScriptでkintone RESET APIを簡単に扱える @kintone/rest-api-client というツールもあり、REST APIを実行する関数だけでなくプラグインの開発に役立つ型定義も含まれています。

この記事では、@kintone/dts-gen@kintone/rest-api-client に含まれる型定義をkintoneプラグイン開発に活用する方法をご紹介します。

@kintone/dts-gen とは

まずはじめに、@kintone/dts-gen がどういったツールかを簡単にご紹介します。cybozu developer networkの記事では次のように紹介されています。

kintone-dts-genは、kintoneアプリのTypeScript型定義ファイル(.d.ts)を生成できるCLIツールです。
このツールを利用すると、kintoneアプリのフィールドの型を一括作成できます。

https://cybozu.dev/ja/kintone/sdk/library/dts-gen/

実際に試してみると、次のような型定義ファイルが生成されます。

fields.d.ts
declare namespace kintone.types {
  interface Fields {
    タイトル: kintone.fieldTypes.SingleLineText;
    本文: kintone.fieldTypes.MultiLineText;
    日付: kintone.fieldTypes.Date;
  }
  interface SavedFields extends Fields {
    $id: kintone.fieldTypes.Id;
    $revision: kintone.fieldTypes.Revision;
    更新者: kintone.fieldTypes.Modifier;
    作成者: kintone.fieldTypes.Creator;
    レコード番号: kintone.fieldTypes.RecordNumber;
    更新日時: kintone.fieldTypes.UpdatedTime;
    作成日時: kintone.fieldTypes.CreatedTime;
  }
}

この型定義は特定のアプリに依存したものであり、アプリ固有のカスタマイズには便利に使える一方、アプリに依存しない汎用的なプラグインの開発には利用できません。プラグインの開発には、@kintone/dts-gen に同梱されている kintone.d.ts が役立ちます。このファイルには kintone.app.record.get のようなkintone JavaScript APIのすべての関数が定義されており、アプリ固有のカスタマイズだけでなく汎用的なプラグインの開発でも利用可能です。

kintone.d.ts(kintone JavaScript APIの一部を抜粋)
declare namespace kintone {
  namespace app {
    namespace record {
      function getId(): number | null;
      function get(): any | null;
      function getHeaderMenuSpaceElement(): HTMLElement | null
    }
  }
}

tsconfig.json で次のように設定すると、IDEやエディタでkintone JavaScript APIの補完機能や型チェックを利用できるようになります。

tsconfig.json
{
  "files": ["./node_modules/@kintone/dts-gen/kintone.d.ts"]
}

フィールドごとの型定義も kintone.d.ts に含まれていますが、レコードが保持するフィールド値に関するものだけで、プラグインの開発でよく利用するフィールドの設定情報に関する型定義は含まれていません。そこで次に必要になってくるのが @kintone/rest-api-client です。

kintone.d.ts(フィールドごとの型定義の一部を抜粋)
declare namespace kintone {
  namespace fieldTypes {
    interface SingleLineText {
      type?: "SINGLE_LINE_TEXT";
      value: string;
      disabled?: boolean;
      error?: string | null;
    }
  }
}

@kintone/rest-api-client とは

@kintone/rest-api-client は通常、kintone REST APIの実行する際に使用します。例えば、プラグイン設定画面でそのアプリのフィールド一覧を取得するプログラムは次のようになります。

import { KintoneRestAPIClient } from "@kintone/rest-api-client"

const fields = await new KintoneRestAPIClient().app.getFormFields({ app: appId }).then((resp) => resp.properties);

ここで、fields の型定義を辿っていくと Record<string, KintoneFormFieldProperty.OneOf> であることがわかります。Recordのキーとなる string はフィールドコード、値となる KintoneFormFieldProperty.OneOf はフィールド定義です。この KintoneFormFieldPropertyKintoneRestAPIClient と同じように exportされており、プラグイン開発時にimportして利用することが可能です。また、KintoneFormFieldProperty.OneOf は次のようにすべてのフィールドタイプの型のいずれかとなります。

KintoneFormFieldProperty.OneOf の一部を抜粋
export type OneOf = RecordNumber | SingleLineText | Number | ...;

KintoneFormFieldProperty.OneOf の利用例

シータグではプラグイン設定画面をReactで開発しており、KintoneFormFieldProperty.OneOf を次のように利用しています。

config.tsx
import { KintoneRestAPIClient, KintoneFormFieldProperty } from "@kintone/rest-api-client";
import { useEffect, useState } from "react";
import { createRoot } from "react-dom/client"

((PLUGIN_ID) => {
  const Config = () => {
    const [fields, setFields] = useState<Record<string, KintoneFormFieldProperty.OneOf>({});

    useEffect(() => {
      new KintoneRestAPIClient().app.getFormFields({ app: kintone.app.getId()!, preview: true })
        .then((resp) => resp.properties)
        .then((properties) => setField(properties));
    }, []);

    return (
      <select>
      {Object.values(fields).map((field) => (
        <option value={field.code}>{field.label}</option>
      ))}
      </select>
    );
  };
  createRoot(document.getElementById("plugin-config")!).render(<Config />);
})(kintone.$PLUGIN_ID)

フィールドタイプ固有の型定義を使用したい場合は、フィールドタイプをチェックした上で次のように型アサーションを利用します。このようにすることでより正確な型定義を使用することができ、効率的かつ保守性の高い開発を実現できます。

const singleLineTextFields = fields.filter((field) => field.type === "SINGLE_LINE_TEXT") as KintoneFormFieldProperty.SingleLineText[]);

利用できるその他の型

KintoneFormFieldProperty と同様に KintoneRecordFieldKintoneFormLayout もexportされており、importして利用することが可能です。

KintoneRecordField はレコードのフィールド値の型定義でレコード取得APIのレスポンスに含まれ、KintoneFormLayout はフォームの型定義でフォームレイアウト取得APIのレスポンスに含まれます。

const record: Record<string, KintoneRecordField.OneOf> = await KintoneRestAPIClient().record.getRecord({ app: appId, id: recordId });
const layout: KintoneFormLayout = KintoneRestAPIClient().app.getFormLayout({ app: appId });

このようにREST APIのレスポンスを変数に代入するだけであれば型を書く必要はありませんが、上で例示したReactの useState のようにあらかじめ変数の型を明示する必要がある場合は KintoneFormFieldPropertyKintoneRecordFieldKintoneFormLayout の利用が便利です。

まとめ

TypeScriptで開発する上で、IDEやエディタによる補完機能や型チェックは非常に強力です。うまく活用できれば開発効率性・保守性・品質の大きな向上につながります。しかし、正確に型を割り当てるのは簡単ではなく、どうしてもうまくいかずに型アサーションで anyunknown を割り当ててしまうこともあります。

型アサーションを用いずに自動で正確な型が割り当てられるのが理想的で、それを実現するためにはライブラリが公式提供している型定義の活用が欠かせません。kintoneのプラグイン開発においては、この記事でご紹介した @kintone/dts-gen@kintone/rest-api-client の利用がオススメです。ぜひ一度お試しください!

株式会社シータグ

Discussion