如何在TypeScript中扩展web3js支持自定义Infura relay方法
TypeScript下扩展web3.js自定义relay属性的解决方法
问题场景
- 基于React + TypeScript搭建自研应用原型,后续计划迁移至Django,选型web3.js方便后续对接web3.py,同时计划接入Infura Relay能力,实现用户无需持有加密钱包即可使用的功能
- 参照Infura ITX教程通过
web3.extend()方法扩展了relay属性及对应查询方法,相同逻辑在JavaScript环境下可正常运行,但TypeScript编译时提示Web3类的类型定义中不存在relay属性,无法通过编译 - 现有初始化逻辑如下:
// 配置Provider let web3 = new Web3( new Web3.providers.HttpProvider( `https://:${process.env.REACT_APP_PROJECT_SECRET}@${process.env.REACT_APP_ETHEREUM_NETWORK}.infura.io/v3/${process.env.REACT_APP_PROJECT_ID}` ) ); web3.extend({ property: "relay", methods: [ { name: "getBalance", call: "relay_getBalance", params: 1 }, ], })
- 预期后续通过回调形式
() => web3.relay.getBalance(...)查询Gas池余额,需要在保留TypeScript技术栈的前提下,实现Web3类类型定义的扩展,正常调用relay相关方法。
可行方案
方案1:TS模块类型补全(推荐,无侵入、保留类型校验)
直接在当前React组件文件顶部,或者项目全局类型声明文件(比如新建src/types/web3.d.ts存放全局扩展),用TS原生的模块补充(Module Augmentation)语法,给Web3类补充relay属性的类型定义即可,不需要改动任何现有业务逻辑:
import type Web3 from 'web3'; // 先定义relay下挂载的方法类型,可根据后续接入的ITX方法持续扩展 interface Web3RelayMethods { // 入参为Gas池绑定的地址,返回值为余额Promise(单位wei,可根据实际接口返回调整类型) getBalance: (address: string) => Promise<string>; } // 扩展web3模块的官方类型定义 declare module 'web3' { interface Web3 { relay: Web3RelayMethods; } }
加完这段声明后,TS就能正常识别web3实例上的relay属性和对应方法,编译不会报错,同时还能保留代码提示、参数校验的能力。后续要新增relay相关方法,只需要在Web3RelayMethods接口里补充对应方法的入参、返回值类型即可。
如果是在全局
.d.ts文件中写类型扩展,不需要手动import Web3类型,直接写declare module块就行。
方案2:封装统一的Web3实例(适合长期维护项目)
把web3初始化、扩展relay的逻辑抽成独立的工具文件,在文件内完成类型处理,后续所有业务代码统一从这个工具文件引入web3实例,不需要每个组件重复写类型扩展:
// src/utils/itxWeb3.ts import Web3 from 'web3'; interface Web3RelayMethods { getBalance: (address: string) => Promise<string>; } // 初始化web3实例 const web3 = new Web3( new Web3.providers.HttpProvider( `https://:${process.env.REACT_APP_PROJECT_SECRET}@${process.env.REACT_APP_ETHEREUM_NETWORK}.infura.io/v3/${process.env.REACT_APP_PROJECT_ID}` ) ); // 挂载relay扩展方法 web3.extend({ property: "relay", methods: [ { name: "getBalance", call: "relay_getBalance", params: 1 }, ], }); // 导出合并类型后的实例 export default web3 as Web3 & { relay: Web3RelayMethods };
组件内使用时直接引入即可,不需要额外做类型处理:
import web3 from '@/utils/itxWeb3'; // 组件内回调逻辑 const fetchGasPoolBalance = async () => { const balance = await web3.relay.getBalance('你的Gas池绑定地址'); console.log('Gas池余额(wei):', balance); }
方案3:临时类型断言(仅适合快速调试)
如果只是临时调试功能,不想写完整类型定义,可以在调用位置直接用类型断言绕开编译校验:
const balance = await (web3 as any).relay.getBalance('Gas池地址');
这个方案不建议在生产环境使用,会丢失TS的类型校验能力,写错方法名、传错参数都不会有编译提示,很容易引发线上问题。
内容的提问来源于stack exchange,提问作者Manu Galán
相关产品推荐
相关产品推荐

