djipco / webmidi

Tame the Web MIDI API. Send and receive MIDI messages with ease. Control instruments with user-friendly functions (playNote, sendPitchBend, etc.). React to MIDI input with simple event listeners (noteon, pitchbend, controlchange, etc.).
Apache License 2.0
1.53k stars 115 forks source link

'WebMidi' only refers to a type, but is being used as a namespace here. #247

Closed maximedupre closed 2 years ago

maximedupre commented 2 years ago

Description

There seems to be an issue with the typings 🤔

Error: node_modules/webmidi/dist/cjs/webmidi.cjs.d.ts:983:26 - error TS2702: 'WebMidi' only refers to a type, but is being used as a namespace here.

983   constructor(midiInput: WebMidi.MIDIInput);
                             ~~~~~~~

Error: node_modules/webmidi/dist/cjs/webmidi.cjs.d.ts:2199:27 - error TS2702: 'WebMidi' only refers to a type, but is being used as a namespace here.

2199   constructor(midiOutput: WebMidi.MIDIOutput);
                               ~~~~~~~

Error: node_modules/webmidi/dist/cjs/webmidi.cjs.d.ts:5385:14 - error TS2702: 'WebMidi' only refers to a type, but is being used as a namespace here.

5385   interface: WebMidi.MIDIAccess;

Environment: Specify the environment where you are witnessing the problem:

djipco commented 2 years ago

I am not getting this error in an environment very similar to yours. Could you provide an example that triggers the issue? Perhaps it's something in your config file?

The webmidi.d.ts file depends on the basic types for the Web MIDI API (MIDIInput and MIDIOutput, for example) which are imported in the package.json file of WEBMIDI.js. Normally, you shouldn't have to worry about that but it's as though the WebMidi namespace from @types/webmidi is being confused with the WebMidi object defined in the library.

maximedupre commented 2 years ago

@djipco It seems like there is some sort of incompatibility with the angular build. I simply created a new angular project and imported WebMidi and the same error occurs.

Repo: https://github.com/maximedupre/web-midi-issue

Run npm start

Vexcited commented 2 years ago

I also have this problem with Vite + React. I use Node v17.3.0 and TypeScript on Linux (Pop!_OS).

The code from the project is also available here: https://github.com/Vexcited/lpadder/blob/9e3122723c06aa052506c52a9e0953214524d6ce/src/pages/utilities/midi-checker.tsx

I have the same error when trying to build my project, so I guess this is not only happening for Angular image

Hope it can help !

Edit: found a workaround by using temporarily webmidi@2.5.3 but I wish v3 will be fixed soon ~

I really want to help but I looked up the code and seems so much harder than I thought 😭

djipco commented 2 years ago

I will try to find some time to look at this this week. Unfortunately, I'm not a TypeScript developer so I feel out of my depth... We need to find a TypeScript expert that's also a WEBMIDI.js fan. Anyone?

joshwilsonvu commented 2 years ago

I'm not super familiar with library type declarations but it seems like you might need something like:

// get types somehow
import type { WebMidi, MIDIInput, MIDIOutput, MIDIAccess } from "...";

// export this type
export WebMidi;

// overlay with a namespace for accessing other types, 
// might be `declare namespace` instead
export namespace WebMidi {
  export MIDIInput;
  export MIDIOutput;
  export MIDIAccess;
}
djipco commented 2 years ago

@joshwilsonvu Thank you so much for your input.

Here is something you probably should know to better understand the problem. By default, TypeScript does not know about the objects from the native Web MIDI API. So, I had to add @types/webmidi as a dependency for the library.

However, the namespace for the native objects (WebMidi) collides with the name of my main WebMidi object. I'm not sure what to do from there... Ideas?

djipco commented 2 years ago

Can I somehow rename the namespace of the imported types (@types/webmidi) to, for example, WebMidiApi ? I could then use WebMidiApi.MIDIOutput without causing a conflict with my own WebMidi object.

The thing is that I'm never explicitly importing the external types. I just added them as a dependency to the project and TypeScript automagically picks them up.

joshwilsonvu commented 2 years ago

Check out this example, to see how the types overlap. WebMidi is both a class and a namespace, so you can use the WebMidi value (a constructor fn), the WebMidi type (an instance of the class), and the WebMidi.MIDIInput type on the namespace.

I think it's likely a problem with how these types are imported/exported. Just a note, if you want to forward @types/webmidi types to your users, it does have to be a dependency, not a devDependency. I don't have much time right now to dig more into this, I'll come back to it by tomorrow.

djipco commented 2 years ago

Thanks to the feedback from @joshwilsonvu, I think I have finally found a solution. This is what I did:

  1. Copy the type definitions for the native Web MIDI API into the head of /typescript/webmidi.d.ts;
  2. Rename the native API's namespace from WebMidi to WebMidiApi;
  3. Adjust all references to the namespace;
  4. Re-compile the type definitions published to /dist;

With the new definitions, I was able to successfully compile the Angular example prepared by @maximedupre (thanks for that, by the way!).

@Vexcited Can you try it out and confirm that it works for you too? If it does, I will publish a new release. All you have to do is replace the following file in your project (node_modules/webmidi/dist/esm/webmidi.esm.d.ts) with the matching one from GitHub.

If others want to try it out, simply replace either the webmidi.esm.d.ts or webmidi.cjs.d.ts file depending on which one fits your project. If you don't know, just replace both.

Vexcited commented 2 years ago

Hello !

I've followed your instructions and it successfully built ! I can confirm that .cjs and .esm works on Vite + React.

djipco commented 2 years ago

Release v3.0.16 fixes this problem. Thanks all for your help!