オリジナル記事
How to Use TypeScript with Salesforce – Part 2
TypeScriptについてご紹介する全2回のシリーズの第2回では、SalesforceエコシステムのさまざまなポイントでTypeScriptを利用できることをご説明します。このブログをお読みになる前に、前回の記事(パート1)に目を通していただくことを強くお勧めします。前回のブログでは、TypeScriptの概要やJavaScriptとの違いについてご紹介しています。
TypeScriptについてご紹介する全2回のシリーズの第2回では、SalesforceエコシステムのさまざまなポイントでTypeScriptを利用できることをご説明します。
このブログをお読みになる前に、前回の記事(パート1)に目を通していただくことを強くお勧めします。前回のブログでは、TypeScriptの概要やJavaScriptとの違いについてご紹介しています。必要とする型の検証の厳密さのレベルを選択することで、プロジェクトにTypeScriptを段階的に導入する方法について解説しました。また、VS Codeが標準のTypeScript言語サービスを提供しており、タイピングの完了時に提案を行ったり、エラーをハイライトしたりすることも説明しました。さらに、型、インターフェース、ジェネリクス、列挙型、モジュールなど、TypeScriptのさまざまな概念についても概要をご紹介しています。
TypeScriptの動作について基本的な内容を理解できたところで、SalesforceでのTypeScriptの利用方法や利用シーンについて確認しましょう。
現在、Lightning Webコンポーネント(LWC)のTypeScriptに対するサポートは限定的です。JavaScriptファイルとHTMLテンプレートの間、あるいは、コンポーネント間で型をチェックすることはできません。たとえば、親コンポーネントから渡されたプロパティの型が子コンポーネントの想定する型と一致するかどうかを確認する方法はありません。また、Salesforce Platformに展開されたLWCバンドル内のTypeScriptファイル(*.ts)を、LWCプラットフォームのコンパイラーが処理することもできません。
しかし、VS CodeのTypeScript言語サービスや、前回のブログでご紹介した厳密さのレベルが低いTypeScriptを利用することで、メリットをすべて享受することが可能です。TypeScriptの宣言ファイルを使って、さまざまなコンポーネントで利用できる型を宣言することもできます。言語サービスでは静的分析を提供するのみであるため、展開に対するコンパイルや追加のステップについて心配する必要はありません。
まずは、JSDocの論理構成を使って、型を指定する必要があります。次に、JavaScriptファイルの最初にフラグ//@ts-checkを追加して、型のチェックを有効化します。これで、以下のスクリーンショットのように、VS Codeが型のエラーをハイライトできるようになりました。
ベストプラクティスは、SFDXプロジェクトのlwcフォルダにあるjsconfig.jsonファイル内のcompilerOptionsにcheckJsフラグを追加して、すべてのLWCコンポーネントで型のチェックを一斉に有効化することです。また、コンパイラーオプションのtargetとしてESNextを追加します(指定しない場合のデフォルトはES3です)。これにより、get/setアクセッサーなどのES5+機能でエラーが生じるのを防ぐことができます。
"compilerOptions": { "experimentalDecorators": true, "checkJs": true, "target": "ESNext" } ...
checkJsフラグを追加したら、それぞれのファイルに//@ts-checkを追加する必要はもうありません。その代わり、特定のファイルでの型のチェックをスキップするため、これらのファイルの最初に//@ts-nocheckフラグを追加します。
Salesforceデータと連携させる場合、連携するそれぞれのオブジェクトについて、型を定義することができます。なお、.sfdx/typings/lwc/sobjects/フォルダ内のオブジェクト定義とは、@salesforce/schema/<objecname>.<fieldname>を介してインポートした個別の項目の型の宣言のことです。これらはSalesforce Extension Packにより自動的に作成されます。これは個別の項目の型を定義しますが、オブジェクト自体の定義は持ちません。
たとえば、取引先責任者のオブジェクト種別は以下のように定義します。
/** * @typedef Contact * @type {object} * @property {string} Id * @property {string} FirstName * @property {string} LastName * @property {boolean} isActive__c - your age. */ export default class Hello extends LightningElement { /** @type {Contact[]} */ contacts; }
VS Codeはこれをピックアップし、型を実行し、自動的に提案を行います。以下のGIFでは、ドット(.)をcontactsの後に入力すると、contactsは配列であるため、at、fill、everyなど複数の配列メソッドが表示されます。しかし、contacts[0]の後にドット(.)を入力すると、先ほど定義した取引先責任者の型のプロパティが表示されます。
あるコンポーネントで定義された型は、別の場所の内部にインポート可能です。以下は、コンポーネントHelloBindingが、Helloコンポーネントで定義された型Contactをインポートする様子を示しています。
export default class HelloBinding extends LightningElement { /** @type {import('../Hello/Hello').Contact} */ result; }
手動でインポートすることなく、すべてのコンポーネントでカスタムの型の定義を利用できるようにするには、プロジェクトのルートでTypeScript宣言ファイル(*.d.ts)を作成し、型エイリアスまたはインターフェースを使って型を宣言します。たとえば、mytypes.d.tsファイルを作成し、2つの型を作ることができます。
interface Contact { id: string, name: string } interface Order { id: string, orderNumber: number }
次に、TypeScriptにこのファイルを確認して型の宣言を検索するよう指示する必要があります。これには、SFDXプロジェクトのlwcフォルダ内にあるjsconfig.jsonファイルのincludeプロパティに、ファイルの場所を追加します。
"include": [ "**/*", "../../../../.sfdx/typings/lwc/**/*.d.ts", "../../../../mytypes.d.ts" ],
これで、JSDoc構文を使って、LWCコンポーネント内のこれらの型をインポートすることなく利用できるようになりました。
/** @type { Contact } */ contacts;
宣言ファイルはソース制御にコミットされるため、プロジェクトのすべての開発者が同じファイルを使用できます。
SFDXプロジェクトの.sfdx/typingsフォルダには、Lightning Message ServiceやLightningデータサービスアダプターなどの標準モジュールに対する定義済みの型宣言も含まれています。フォルダの場所は、jsconfig.jsonのincludeプロパティにも表示されるため、コンポーネントでこれらの定義済みの型を使用することができます。たとえば、getRecordの操作の結果が特定のシェイプを持ちます。すなわち、クエリするオブジェクトにもとづき変更されることのない、定義済みのプロパティを持ちます。VS Codeの提案を使って、このような型の推論やインポートが可能です。
より厳密な型のチェックを行いたい、あるいは.tsファイルを使ってコンポーネントを作成したい場合、コンポーネントをSalesforce Platformに展開する前に、まずTypeScriptコンパイラーを実行する必要があります。オプションの1つに、tscコマンドを実行してTypeScriptファイルをJavaScriptファイルにコンパイルするpredeploy Salesforce CLIフックの作成があります。
Lightning Web Runtime(LWR)を使って、プラットフォーム外でシングルページアプリケーションを作成する場合、プロジェクトの作成中に、LWCのTypeScript変数の使用を選択できます。
これにより、JavaScriptの代わりに、Lightning WebコンポーネントのTypeScriptファイル(*.ts)を使用するプロジェクトが作成されます。LWRはこれらのファイルを自動的にコンパイルして、クライアント(ブラウザー)に送信します。まだ作成していない場合は、プロジェクトのルートにtsconfig.jsonファイルを必ず作成してください。
以下は、標準の型とカスタムインターフェースを使った簡単なTypeScript LWCコンポーネントの例です。Line 17はランタイム時に正常に実行されていますが、この型に無効なプロパティを設定しようとしたためにTypeScriptはこれをエラーと見なしています。
この場合でも、実装をサポートする独自の型宣言ファイルを作成することができます。ベストプラクティスは、プロジェクトのルートにtypesフォルダを作成し、このフォルダに*.d.tsファイルを保存することです。
// my.d.ts interface Contact{ id: string, name: string }
次に、tsconfig.jsonのincludeプロパティにtypesフォルダのパスを追加します。
{ "include": ["src/**/*", "types/*"] }
これで、コンポーネント内でこれらのカスタム型を使用できるようになりました。
export default class HelloWorldApp extends LightningElement { con: Contact; }
TypeScriptはSalesforce機能を作成するサポート言語の1つで、generate functionコマンドの実行時にtypescriptを-lフラグに渡します。
sf generate function -n myfunction -l typescript
このコマンドは、index.tsやtsconfig.jsonなどのファイルを備える一般的なTypeScriptプロジェクトのように構成されたTypeScript機能を作成します。
さらに、TypeScript機能は、package.jsonファイル内の依存関係として、Node.js用Salesforce機能のSDK(sf-fx-sdk-nodejs)を包含します。このSDKは、コードにインポートおよび使用できる、定義済みのさまざまな型とインターフェースをまとめて提供します。これらの定義済みの型により、パラメーターのシェイプ、プロパティ、リターンタイプを正確に把握し、Salesforceデータとの連携を大幅に簡素化できます。
独自の型を作成したり、サードパーティのライブラリを使用している場合はそこから型をインポートしたりすることも可能です。たとえば、npmからnode-fetchライブラリをインストールして(ドキュメントを参照してください)、ライブラリが定義した型を使用できます。以下の例では、ライブラリからReferrerPolicy型をインポートして使用しています。
コンテキストに付加されたSalesforce組織情報を返すTypeScript機能レシピもご確認ください。
@salesforce/ts-types
ライブラリ@salesforce/ts-typesライブラリ(ドキュメントを参照してください)は、簡潔な型ガードの型の絞り込み機能のコレクションと、一般的に望ましい型のコレクションを提供します。たとえば、AnyJson型は型指定されていないJSONオブジェクトを保持するために使用することができ、hasBoolean、isBoolean、getBooleanなどのさまざまな機能はJSONオブジェクトの特定の型の値を得ることができます。
このライブラリを活用することで、ランタイム型の安全性を確保できます。次に例を示します。
import {AnyJson, asJsonArray, ensureJsonMap, hasBoolean, getBoolean } from "@salesforce/ts-types"; const response: AnyJson = [{ start: 0, success: true, results: [{ name: 'first' }, { name: 'second' }] }]; const results = asJsonArray(response, []); results.forEach(item=>{ let record = ensureJsonMap(item); if(hasBoolean(record, 'success')){ let isSuccessful = getBoolean(record, 'success') // type of isSuccessful is boolean } });
Salesforce CLIプラグインは、TypeScriptを使ってOCLIF上に構築されています。このプラグインは、先ほどご説明した@salesforce/ts-typesライブラリも使用しています。query機能をJSforceライブラリから使用する場合に動作するジェネリクスの概念についてもご確認いただけます。以下の例では、カスタムのOrganizationインターフェースを作成し、ジェネリクスのquery機能に対する型として明示的に設定する方法をご覧いただけます。これにより、Organizationに対するresultの型が自動的に設定されます。
public async run(): Promise<AnyJson> { ... const query = 'Select Name, TrialExpirationDate from Organization'; // The type we are querying for interface Organization { Name: string; TrialExpirationDate: string; } // Query the org const result = await conn.query<Organization>(query); ... }
Slackアプリケーションを構築する方法はさまざまですが、その中の1つが、依存関係として追加された@slack/boltライブラリとNode.jsを使用するやり方です。追加の依存関係としてtypescriptを設定し、TypeScriptを使ってSlackアプリケーションを簡単に構築できます。TypeScriptを使って構築したBoltアプリケーションのサンプルをここからご確認いただけます。Node.js用Salesforce機能のSDKと同様、Boltライブラリも、SlashCommand、SlackEvent、MessageEvent、BlockActionなど多数の定義済みの型を備えています。SlackでのTypeScriptの使用について、ドキュメント作成が進んでいますので、最新情報をぜひチェックしてください。
多数のSalesforce製品で、さまざまなレベルの厳密さでTypeScriptを利用する機会は次第に増えつつあります。このブログで、そのような機会の概要を知っていただければ幸いです。ランタイムのエラーを低減させる堅牢なコードを作成する際に、TypeScriptがいかに役立つかをご理解いただけたことと思います。ぜひTypeScriptをお試しください。
今後の展開に役立つリソースをいくつかご紹介します。
Aditya Naag Topalliは、Salesforceの14x Certified Lead Developer Advocateです。動画やウェブセミナー、ブログ、オープンソースへの貢献を通じ、Salesforceエコシステム内外の開発者のスキル強化や指導に取り組んでいます。世界中のカンファレンスやイベントでの講演も頻繁に行っています。AdityaのTwitterやLinkedInのアカウントをぜひフォローしてください。GitHubでの取り組みもご覧ください。