Index
前言
为了更加便捷地在kintone中使用TypeScript,这次我们将演练如何使用@kintone/dts-gen和kintone JavaScript Client (@kintone/rest-api-client)这两个工具来编写TypeScript。
关于如何用TypeScript来自定义kintone的基本方法,本网站的另一篇文章使用TypeScript开发kintone自定义中有所介绍。大家可以自行参考。
什么是TypeScript?TypeScript的优点
TypeScript是Microsoft开发的开源编程软件。使得在JavaScript中可以添加变量的类型。
“类型”指的是为了明确变量中所赋的值,是数值,还是字符串等种类而事先声明的。
除了数值、字符串等基本类型以外,用户还可以声明自定义类型。
因为有了“类型”信息,所以即使不去读取变量中的数据,也可以在编写代码时就知道数据类型,这样可以避免很多bug的产生。
例如:从kintone API中获取到的数值、计算字段的数字其实不是数值型而是字符串,直接进行乘法运算的话就会被预判为错误。
对数值、计算字段直接进行乘法运报错的例子
计算字段的「合计金额」 "record.合计金额.value * 0.1" 中的乘法运算会显示为error。
除此之外,类似对象中没有指定的key之类的也会被IDE判断为error。
访问对象时error的例子。
给对象 "record.字符串.value" 赋值时,由于实际上不存在“字符串”这个字段,所以显示了error。
TypeScript准备了kintone应用中各个字段的类型信息,基于这些信息,可以避免上述kintone字段代码的错误输入。
特别是处理表格中深层复杂的结构、REST API 的request参数、response时可以发挥明显的效果。
实际操作起来会如何呢?让我们来试一下。
准备
代码:https://github.com/cybozudevnet/sample-kintone-webpack-for-intermediate
通过点击绿色按钮可获得链接地址来执行 git clone 或下载 Zip 文件。
有关后续部署,请参阅上面的自述。
上述代码的URL在向JavaScript自定义中级开发者的目标前进(1) 〜webpack篇〜到(4)中所提到的都是同一代码。
在之前的文章中已经试过的用户,为以防万一,可以在目录下再次执行 npm install。
设置的细节之后会介绍,到此为止使用TypeScript所需要的包都安装完毕了。
范例
我们使用第4篇文章向JavaScript自定义中级开发者的目标前进(4)〜kintone REST API Client篇〜中的范例,就如何用TypeScript改写来演示一下。
使用到的应用也是相同的,所以请大家按第4篇来配置好应用。
“类型”信息的获取
在实际编写代码前,需要事先定义好应用中的字段类型。使用 @kintone/dts-gen 这个库来获取应用字段的类型。
执行下列代码,获取报价单的字段类型。所生成的文件叫做类型定义文件。
在此范例中,我们已经放置了类型定义文件。 通过执行下列代码,您环境中的应用的字段类型会覆盖类型定义文件。
npx @kintone/dts-gen --host https://kintone的域名.cybozu.cn/ -u 用户名 -p 密码 --app-id 应用ID --type-name Quote --namespace KintoneTypes -o src/types/Quote.d.ts
这次的范例中,使用了报价单应用和商品应用,但只用JavaScriptAPI改写报价单应用的值、所以用 @kintone/dts-gen 只生成了报价单应用字段的类型定义文件。商品应用使用REST API来改写,所以需要另外在范例大妈中定义类型。
命令执行成功后,会生成像下面这样的文件
文件: src/types/Quote.d.ts
declare namespace KintoneTypes { interface Quote { 备注: kintone.fieldTypes.MultiLineText; 报价日期: kintone.fieldTypes.Date; 地址: kintone.fieldTypes.SingleLineText; 报价单号: kintone.fieldTypes.SingleLineText; 合计金额: kintone.fieldTypes.Calc; 报价明细: { type: "SUBTABLE"; value: { id: string; value: { 数量: kintone.fieldTypes.Number; 型号: kintone.fieldTypes.SingleLineText; price: kintone.fieldTypes.Number; 商品名: kintone.fieldTypes.SingleLineText; 小计: kintone.fieldTypes.Calc; }; }[]; }; } interface SavedQuote extends Quote { $id: kintone.fieldTypes.Id; $revision: kintone.fieldTypes.Revision; 更新人: kintone.fieldTypes.Modifier; 创建人: kintone.fieldTypes.Creator; 记录编号: kintone.fieldTypes.RecordNumber; 更新时间: kintone.fieldTypes.UpdatedTime; 创建时间: kintone.fieldTypes.CreatedTime; } }
范例代码
文件: src/apps/quote_ts/index.ts
import {KintoneRestAPIClient, KintoneRecordField} from '@kintone/rest-api-client'; // 商品应用的类型定义 type SavedProduct = { $id: KintoneRecordField.ID; $revision: KintoneRecordField.Revision; 更新人: KintoneRecordField.Modifier; 创建人: KintoneRecordField.Creator; 记录编号: KintoneRecordField.RecordNumber; 更新时间: KintoneRecordField.UpdatedTime; 创建时间: KintoneRecordField.CreatedTime; service_type: KintoneRecordField.RadioButton; note: KintoneRecordField.MultiLineText; 型号: KintoneRecordField.SingleLineText; product_name: KintoneRecordField.SingleLineText; price: KintoneRecordField.Number; 在库数量: KintoneRecordField.Number; } // 请输入商品列表的应用ID const productsAppId = 122; const events = ['app.record.create.submit', 'app.record.edit.submit']; kintone.events.on(events, async (event) => { const record = event.record as KintoneTypes.Quote; record.合计金额.value // 创建一个连接kintone的实例 const client = new KintoneRestAPIClient({}); // 这次为了简便,表中的商品不允许重复。 // 只是简易的重复检查,不理解也没关系。 const hasDuplicatedRow = record.报价明细.value.some((rowA, indexA, arr) => { return arr.find( (rowB, indexB) => indexA !== indexB && rowA.value.型号.value === rowB.value.型号.value ); }); if (hasDuplicatedRow) { event.error = '不允许选择重复的商品'; return event; } // 获取表中的商品记录 let products; try { // 通过指定泛型,在使用products变量时,可以推断出类型 products = await client.record.getRecords({ app: productsAppId, query: `型号 in (${record.报价明细.value .map((row) => `"${row.value.型号.value}"`) .join(', ')})`, }); } catch (error) { event.error = '获取记录失败'; return event; } // 在商品列表的库存中减去相应数量 const deductedProductRecords = products.records.map((productRecord) => { const tableRow = record.报价明细.value.find( (row) => productRecord.型号.value === row.value.型号.value ); // 存放型号值和计算后的库存值 return { 型号: { value: productRecord.型号.value, }, 在库数量: { value: Number(productRecord.在库数量.value) - Number(tableRow?.value.数量.value), }, }; }); // 计算后的库存值是否有小于0的情况 const noStockRecords = deductedProductRecords.filter( (productRecord) => Number(productRecord.在库数量.value) < 0 ); // 存在1条以上记录时报错并跳过保存 if (noStockRecords.length > 0) { // event.error中存放错误信息后返回 // 列出出问题的商品型号 event.error = `存在库存不够的商品。型号 ${noStockRecords .map((productRecord) => productRecord.型号.value) .join(', ')}`; return event; } // 没有问题的话更新 try { await client.record.updateRecords({ app: productsAppId, records: deductedProductRecords.map((productRecord) => { return { updateKey: { field: '型号', value: productRecord.型号.value, }, record: { 在库数量: { value: productRecord.在库数量.value, }, }, }; }), }); } catch (error) { event.error = `更新失败 ${error.message}`; return event; } return event; });
请实际编辑一下,尝试查看27行附近的record中的内容,然后看看Visual Studio Code会发生什么吧。
就像下方的图片那样,输入「record.」后,报价应用的字段就会出现在提示框中了。
范例代码的说明
由于篇幅关系不能表尽TypeScript,在此就范例代码大致要点做一些介绍。
其实,和第四篇所介绍的代码基本没有什么不同。如下所示,只有类型信息的处理上有所不同。
第26行: 申明event.record的类型信息
const record = event.record as kintoneTypes.Quote;
这里指的是之前讲到的用 @kintone/dts-gen 所作成的类型。这么做可以定义「event.record 是报价应用的的记录哦」这件事。
这就是所谓的类型断言(日文)第4行〜第18行: 产品应用的类型定义(@kintone/rest-api-client)
实际上@kintone/rest-api-client是支持TypeScript的。
但是,@kintone/rest-api-client 的类型定义不是像 @kintone/dts-gen 一样是用命令行完成的。
所以,必须像这样准备好产品应用自身的类型。
@kintone/rest-api-client的类型定义的详细方法请参照这里第48行: 向 @kintone/rest-api-client 里传递类型信息
products = await client.record.getRecords<SavedProduct>({
这一行,通过把第4行~第18行所定义的产品应用的类型信息(SavedProduct)传递过去,告诉我们得到getRecords()所返回的记录是SavedProduct类型。
这样,REST API 所返回的记录就会得到提示框的支持了。
范例代码的编译
我们不能在浏览器里直接执行TypeScript,但可以通过webpack把TypeScript转换成JavaScript。
输入以下命令,转换完之后再上传。
npx webpack --mode production
详细信息请参照向JavaScript自定义中级开发者的目标前进(3) 〜自动批量上传文件篇〜还具有自动上传功能。
结束语
把 TypeScript 和 kintone 的生态环境相互酝酿结合,得到了相当不错的开发体验。
在处理 kintone 字段的过程中,不可避免地需要进行对照字段代码等繁琐又容易出错的工作。而使用 TypeScript 的特性,便可将此防范于未然,对于开发者来说是一大福音。
如果对本篇文章感兴趣的话,十分建议借此契机开始学习 TypeScript。TypeScript 如今备受瞩目,相信将来一定可以在开发中起到非常重要的作用。
该Tips在 kintone 2021年2月版,@kintone/rest-api-client@1.10.0 中进行过确认。