Skip to content
This repository was archived by the owner on Mar 5, 2025. It is now read-only.

How to use [email protected] with TypeScript? #1597

Closed
ghost opened this issue Apr 30, 2018 · 38 comments
Closed

How to use [email protected] with TypeScript? #1597

ghost opened this issue Apr 30, 2018 · 38 comments

Comments

@ghost
Copy link

ghost commented Apr 30, 2018

Here is a minimal code for testing:

import * as Web3 from "web3"

const web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:7545"))

const main = async () => {
  const blockNumber = await web3.eth.getBlockNumber()
  console.log({ blockNumber })
}

main().catch(err => console.error(err))

The module typings are wrong. If I try to compile it, there are some errors:

maxblock@mbp t10-web3-typescript *$ npm run build

> [email protected] build /Users/maxblock/projects/test/t10-web3-typescript
> rimraf dist && tsc

../../../node_modules/web3/types.d.ts(1,27): error TS7016: Could not find a declaration file for module 'bn.js'. '/Users/maxblock/node_modules/bn.js/lib/bn.js' implicitly has an 'any' type.
  Try `npm install @types/bn.js` if it exists or add a new declaration (.d.ts) file containing `declare module 'bn.js';`
../../../node_modules/web3/types.d.ts(2,21): error TS7016: Could not find a declaration file for module 'underscore'. '/Users/maxblock/node_modules/underscore/underscore.js' implicitly has an 'any' type.
  Try `npm install @types/underscore` if it exists or add a new declaration (.d.ts) file containing `declare module 'underscore';`
src/index.ts(3,14): error TS2351: Cannot use 'new' with an expression whose type lacks a call or construct signature.
src/index.ts(3,32): error TS2339: Property 'providers' does not exist on type 'typeof "/Users/maxblock/node_modules/web3/index"'.

OK. Next, I tried to override typings with my own. For this reason, I've created a file typings.d.ts with simple stub: declare module "web3".

There are fewer errors with my own typings, but anyway I can't compile it:

maxblock@mbp t10-web3-typescript *$ npm run build

> [email protected] build /Users/maxblock/projects/test/t10-web3-typescript
> rimraf dist && rimraf node_modules/web3/index.d.ts && rimraf node_modules/web3/types.d.ts && tsc

../../../node_modules/web3/types.d.ts(1,27): error TS7016: Could not find a declaration file for module 'bn.js'. '/Users/maxblock/node_modules/bn.js/lib/bn.js' implicitly has an 'any' type.
  Try `npm install @types/bn.js` if it exists or add a new declaration (.d.ts) file containing `declare module 'bn.js';`
../../../node_modules/web3/types.d.ts(2,21): error TS7016: Could not find a declaration file for module 'underscore'. '/Users/maxblock/node_modules/underscore/underscore.js' implicitly has an 'any' type.
  Try `npm install @types/underscore` if it exists or add a new declaration (.d.ts) file containing `declare module 'underscore';`

I've even tried deleting node_modules/web3/index.d.ts and node_modules/web3/types.d.ts files, but without success.

How do you work with web3.js + TypeScript?

P.S.
If don't use import syntax and get Web3 like const Web3 = require("web3"), it can be compiled.

@ghost ghost changed the title How to use web3.js with TypeScript? How to use [email protected] with TypeScript? Apr 30, 2018
@Spaider15
Copy link

Spaider15 commented May 2, 2018

Same issue, you need to delete first to imports inside web3 folder in types.d.ts, and they usages, BigNumber and underscore.
But this is temporary workaround

@robrobbins
Copy link
Contributor

robrobbins commented May 9, 2018

I'm currently spec'ing out a TCR with web3 beta and TS. Feel free to checkout the spec directory for some things we are doing at Computable with these 2 (web3/TS).

https://github.com/computablelabs/computable.js

I'm putting a couple PRs together now for TS corrections...

@pau1m
Copy link

pau1m commented May 17, 2018

@Spaider15 Could you clarify how to remove usages?

@Spaider15
Copy link

@pau1m Find in file where they are mentioned and just remove it

@levino
Copy link
Contributor

levino commented May 25, 2018

Hey all. I am sorry but I cannot follow. Can someone please reiterate for dummies like me on how to fix this? Thanks you very much in advance.

@ghost
Copy link
Author

ghost commented May 25, 2018

@levino

As workaround you can use require syntax: const Web3 = require("web3"); instead of import.

There will be no typings, but it compiles.

@jintoppy
Copy link

jintoppy commented May 27, 2018

I am able to solve the above mentioned issue by importing as default.

import Web3 from "web3";

Also, you may need to set
"noImplicitAny": false
in tsconfig.json.

@ghost
Copy link
Author

ghost commented May 28, 2018

@jintoppy Yes, it really compiles with "noImplicitAny": false.

But for me it only compiles, can't run.
Here is a demo code:

import Web3 from "web3"
// const Web3 = require("web3")
const web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:7545"))

const main = async () => {
  const blockNumber = await web3.eth.getBlockNumber()
  console.log({ blockNumber })
}

main().catch(err => console.error(err))

When I run it, there is an error:

maxblock@mbp t10-web3-typescript (master)*$ node dist
/Users/maxblock/projects/test/t10-web3-typescript/dist/index.js:13
const web3 = new web3_1.default(new web3_1.default.providers.HttpProvider("http://localhost:7545"));
                                                   ^

TypeError: Cannot read property 'providers' of undefined
    at Object.<anonymous> (/Users/maxblock/projects/test/t10-web3-typescript/dist/index.js:13:52)
    at Module._compile (internal/modules/cjs/loader.js:702:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:713:10)
    at Module.load (internal/modules/cjs/loader.js:612:32)

Here is a link on repo with this example. Can you please check, does it work for you?

https://github.com/max-block/t10-web3-typescript

@levino
Copy link
Contributor

levino commented May 28, 2018

The correct syntax to import Web3 is

import Web3 = require('web3')

because the module uses

module.exports = Web3;

However the type definitions are incorrect:
https://github.com/ethereum/web3.js/blob/c0e727eac7c16826c39fd43a03a4930b5ff6fff8/packages/web3/index.d.ts#L27
which should be

export = Web3;

@levino
Copy link
Contributor

levino commented May 28, 2018

Here is the commit breaking the type definitions c0a4f0a

@ghost
Copy link
Author

ghost commented May 28, 2018

@levino
import Web3 = require('web3')

This doesn't work for me, when I compile it, there is an error:

maxblock@mbp t10-web3-typescript (master)*$ npm run build

> [email protected] build /Users/maxblock/projects/test/t10-web3-typescript
> rimraf dist && tsc

src/index.ts(3,14): error TS2351: Cannot use 'new' with an expression whose type lacks a call or construct signature.
src/index.ts(3,32): error TS2339: Property 'providers' does not exist on type 'typeof "/Users/maxblock/projects/test/t10-web3-typescript/node_modules/web3/index"'.

@levino
Copy link
Contributor

levino commented May 28, 2018

Which type definitions are you using? The ones from here are broken, as I am saying. It cannot work. You have to fix them by hand.

@levino
Copy link
Contributor

levino commented May 29, 2018

Okay I "fixed" it for me locally until this madness is resolved properly.
First you have to add a postinstall script in your package.json like so:

{
  "postinstall": "rm -f node_modules/web3/index.d.ts"
}

This will remove the incorrect type definitions shipped with web3.
Then you need to add these two module augmentation files to your codebase and make sure typescript loads them. For example put them in a folder typings and add to your tsconfig.json:

{
  "typeRoots": [
    "./node_modules/@types/",
    "./typings/"
  ]
}

@wmira
Copy link

wmira commented May 29, 2018

my web3 typescript project works fine.. https://github.com/wmira/ethereum-notification/blob/master/src/index.ts#L2

@levino
Copy link
Contributor

levino commented May 29, 2018

What does your tsconfig look like?

@levino
Copy link
Contributor

levino commented May 29, 2018

You use esModuleInterop to make it work. It will solve the following problem:

Similarly a default import (i.e. import d from "foo") for a CommonJS/AMD/UMD module as equivalent to const d = require("foo").default.Most of the CommonJS/AMD/UMD modules available today do not have a default export, making this import pattern practically unusable to import non-ES modules (i.e. CommonJS/AMD/UMD). For instance import fs from "fs" or import express from "express" are not allowed.

While it might work for your case, I am not sure whether this implicit mapping also correctly works for the exported type. Like doing something like this

import Web3 from 'web3'

let web3: Web3

There simply is a mismatch between the actual export object and what the type declarations say ( that export has a key default with a value, which is a lie).

@wmira
Copy link

wmira commented May 29, 2018

have you tried?
import { default as Web3 } from "web3"

my tsconfig is on that repo.

@levino
Copy link
Contributor

levino commented May 29, 2018

You also have "noImplicitAny": false which, no offense intended, disqualifies your repo as a reference implementation.

When you turn noImplictAny on you get a second problem:
https://github.com/ethereum/web3.js/blob/c0e727eac7c16826c39fd43a03a4930b5ff6fff8/packages/web3/types.d.ts#L1
This cannot work because there are not type definitions for bn.js. So only if bn.js is an any you can destructure this type from it, which obviously is any too then.

This package must work with a default and best practice typescript configuration. It is also possible as I have shown above.

@wmira
Copy link

wmira commented May 29, 2018

@levino use the declaration below:

declare module 'bn.js' {

import { Buffer } from 'buffer'

type Endianness = 'le'|'be'

class BN {
constructor(number: number|string|number[]|Buffer, base?: number, endian?: Endianness)
clone(): BN
toString(base?: number, length?: number): string
toNumber(): number
toJSON(): string
toArray(endian?: Endianness, length?: number): number[]
toBuffer(endian?: Endianness, length?: number): Buffer
bitLength(): number
zeroBits(): number
byteLength(): number
isNeg(): boolean
isEven(): boolean
isOdd(): boolean
isZero(): boolean
cmp(b: any): number
lt(b: any): boolean
lte(b: any): boolean
gt(b: any): boolean
gte(b: any): boolean
eq(b: any): boolean
isBN(b: any): boolean

neg(): BN
abs(): BN
add(b: BN): BN
sub(b: BN): BN
mul(b: BN): BN
sqr(): BN
pow(b: BN): BN
div(b: BN): BN
mod(b: BN): BN
divRound(b: BN): BN

or(b: BN): BN
and(b: BN): BN
xor(b: BN): BN
setn(b: number): BN
shln(b: number): BN
shrn(b: number): BN
testn(b: number): boolean
maskn(b: number): BN
bincn(b: number): BN
notn(w: number): BN

gcd(b: BN): BN
egcd(b: BN): { a: BN, b: BN, gcd: BN }
invm(b: BN): BN

}

export type BigNumber = BN

}

Taken from https://github.com/indutny/bn.js/blob/master/lib/bn.js .
I have changed export to BigNumber as expected by web3

@levino
Copy link
Contributor

levino commented May 29, 2018

Dear @wmira, above I suggested a solution for the problem by replacing the non-working type definitions from the web3 package with working ones (which must obviously include type definitions for bn.js). Please also check the gist that I linked.

I also have been running around this repo trying to point the problems out. Seems everybody is sleeping. While I wait for someone to wake up I opened a PR to DefinitelyTyped to have high quality, linted and tested type definitions for web3 in the future.

I appreciate your follow up on the matter but I think we might also have a little bit of a misunderstanding here. I think the issue at hand is resolved. As for your repo, try removing this line. You then have to import Web3 via import Web3 = require('web3') but this will break because the type defintions are incorrect as explained above.

Also if you have to add typings for a dependency of web3 - namelybn.js - by hand (by copying and pasting have baked type definitions from somewhere in the web) this is a - excuse my bluntness - shit setup. This is why I also included the types you referenced in the PR to DefinitelyTyped so in the future bn.js type defintions will also be available via @types/bn.js.

I see that you know what you are doing when it comes to typescript. I would really appreciate a review or some further contributions (like more test code) to the PR in DefinitelyTyped and some support for my move to remove the type definitions from here.

@wmira
Copy link

wmira commented May 30, 2018

@levino i know where you are coming from but sometimes we need to get things going as we need to move and write code. The copy paste of the BN typings is not for the sake of doing so -- obviously a better type can be created but that is not the point here.

I'm sure the people here are busy and so are you so lets take it easy please.

@levino
Copy link
Contributor

levino commented May 30, 2018

I am taking it easy. I did not mean any offense. I understand that everyone is busy and I have no problem to wait until the this topic is picked up by a maintainer (I think sleeping well and long is very important and was not implying that anyone is lazy, but just not working at the moment, maybe they have a team vacation or something). I am merely offering my help. BTW @types/bn.js has been merged into master of DT so it is available soon.

@levino
Copy link
Contributor

levino commented May 30, 2018

Also type definitions for web-eth-abi have been merged. This is going to be a clusterf**k if nobody comes up with a consistent plan soon.

@levino
Copy link
Contributor

levino commented May 30, 2018

Also highlighting @LogvinovLeon to get his view on the matter.

@wmira
Copy link

wmira commented May 30, 2018

@levino are you able to just do a pull request to fix the type definitions here? instead of removing them?

@LogvinovLeon
Copy link
Contributor

@levino Correct me if I'm wrong, but as far as I know - web3 maintainers don't see a value in using/supporting typescript. They can sporadically accept some PR's but don't have the expertice/motivation to do that. I think it will make more sense to keep the typings separately in that case (in DT) so that a review process is much more quick (DT maintainers are much faster). I'm now migrating our typings repository from 0x monorepo to DT. We have full web3 types that us and a bunch of other projects have been using for more than a year. https://github.com/0xProject/0x-monorepo/blob/v2-prototype/packages/typescript-typings/types/web3/index.d.ts

I'm planning to introduce ethereum-types as a general typings package that contains package/agnostic things (like Provider, Block, Unit), because it can be shared later between all ethereum-related packages.

The only downside is that it supports only the latest stable version of web3. I think the beta branch is too immature.

@levino
Copy link
Contributor

levino commented Jun 4, 2018

@wmira Yes, I could. But I do not want to. I think it is better to stop providing type definitions here.

@LogvinovLeon

Correct me if I'm wrong, but as far as I know - web3 maintainers don't see a value in using/supporting typescript. They can sporadically accept some PR's but don't have the expertice/motivation to do that.

They do accept PRs without understanding the consquences and without proper review. Also no linting is been applied, not to even think about tests for the type definitions. This repo is currently following an anti pattern. This is why I am pushing for the removal of the type definitions.

Off topic: What are your issues with web3-1.0.0?

@LogvinovLeon
Copy link
Contributor

@levino Got it. Then I think removing types will be beneficial.
Off topic: No support for ABI v2 (there is an open issue, but it's still open). Missing/lacking/outdated docs. No type definitions. I think for a library that deals with security-critical stuff not having a proper type definition is a no-go.

@levino
Copy link
Contributor

levino commented Jun 8, 2018

Here is an instruction on how to use [email protected] with typescript correctly. This issue right here should be closed as resolved. Whether or not type definitions should remain part of this package should be resolved via #1658

@nivida nivida closed this as completed Aug 9, 2018
@krzkaczor
Copy link

Btw. if anyone is curious how to work with smart contracts in a type-safe manner, TypeChain supports it! Just use: web3-1.0.0 target.

@kael-shipman
Copy link

Note: Per the comment at the very end of the chain that @levino has linked above (here), it appears as though type definitions are now once again shipped with web3? @levino , feel free to correct me if I'm wrong....

@vaneeckhoutnicolas
Copy link

The best is to create a plugin to share through your components.
Anyway if in a specific component you can add this for web3 (same for window.ethereum if needed, e.g. authorize metamask)
declare global {
interface Window { web3: any; }
}

window.web3 = window.web3 || {};

@levino
Copy link
Contributor

levino commented Oct 9, 2020

Or you just use JavaScript if you do not need types...

@nvegater
Copy link

nvegater commented Nov 6, 2020

Anyone has experience with the Typechain?
Does it keep up with w3 changes?

@levino
Copy link
Contributor

levino commented Nov 6, 2020

Typechain does not solve this problem. It is for something else (typing the methods of the Contract instance from web3).

I personally recommend ethers these days. It is not perfect but much better than web3.

@trieulethe
Copy link

"esModuleInterop": true in tsconfig.json and import Web3 from 'web3';
i use this and it work 👍

@SalamandraDevs
Copy link

SalamandraDevs commented Apr 10, 2023

Okay I "fixed" it for me locally until this madness is resolved properly. First you have to add a postinstall script in your package.json like so:

{
  "postinstall": "rm -f node_modules/web3/index.d.ts"
}

This will remove the incorrect type definitions shipped with web3. Then you need to add these two module augmentation files to your codebase and make sure typescript loads them. For example put them in a folder typings and add to your tsconfig.json:

{
  "typeRoots": [
    "./node_modules/@types/",
    "./typings/"
  ]
}

This workaround is currently working for Web3.js v1.9.0, in times when the web3.js team are preparing their v4.0.0 to be relased soon. I tested v4 and the problems are worst in Typescript with ESM, I do not know what to think, but I am using web3 instead of ethers because actually web3 retreives data from JsonRPC faster than ethers v5.

@levino thanks you for this contribution.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests