相信大家在使用js/ts开发Dapp时应该都有使用web3.js或ethers.js。笔者用web3.js更多更熟练,最近也有使用ethers.js,但从开发体验上来说两者其实也没太大差别。实际上,在js/ts上写合约调用的时候,无论是用web3.js还是ethers.js,都非常不方便——因为他们都缺少一个功能:代码补全!
可能我描述的不是很清楚,那我举个栗子就明白了,以web3.js为例:
// 一个示例合约ABI(为了文章的整体可读性,我故意将其压缩为一行)
// 只需要知道我们通过ABI能了解到该合约定义了updateMessage, getMessage两个函数就行
const abi = [{"constant":true,"inputs":[],"name":"getMessage","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_newMessage","type":"string"}],"name":"updateMessage","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"inputs":[{"name":"_initialMessage","type":"string"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"}];
// 合约地址
const address = "0x123456";
// 初始化Web3对象
const web3 = new Web3('<https://provider.url>');
// 初始化合约对象,后续可以使用该对象来调用合约
const contract = new web3.eth.Contract(abi, address);
// 调用合约函数:updateMessage(0x123是你的钱包地址)
const tx = await contract.methods.updateMessage("Hello world").send({from: "0x123"})
// 调用合约函数:getMessage
const message = await contract.methods.getMessage().call();
console.log("Message is", message);
在写上面代码过程中,虽然合约的信息都在ABI中给出,但是我们输入contract.method.之后并不会给出getMessage、updateMessage之类的代码补全(如图所示)

如果我交互的是一个很复杂的合约,(比如前一篇博客中说的GMX,他有几十个合约,一个合约里有几十个函数)在没有代码补全的帮助下,是非常难受的!
当然,除了调用合约函数,我们还有监听合约事件的需求,这个就更棘手了……
在js中没有代码补全还情有可原,但是ts都没有,这是对~~世界上最好的语言——~~TypeScript的侮辱!
当然,这种低级问题,我认为肯定早就有人想到并解决了,但可惜的是,目前我还没有找到(如果大家有可以安利一下)。所以,我决定自己动手,丰衣足食!TypeScript~~作为世界上最好的语言,~~没有什么不可能!
我们想要的效果(最终实现的效果)是这样的(Send):

或者这样的(Call):

甚至这样的(Event Subscription):

此外,为了在有很多合约的情形下方便管理各个合约的地址和ABI,我们还需要有一个记录所有合约地址的JSON文件以及一个统一获取合约入口,直接通过合约名和来获取其地址和ABI并实例化一个合约对象(即上面演示中的getContract函数)
可以看出,在有代码补全的加持下,我们可以非常方便地进行合约调用,不需要反复查看合约文档或者代码,而且自带类型检查,减少潜在BUG,这实在是泰裤辣!