Trying to switch to Typescript under Jovo v3


#1

I’m in the process of prepping my code to cut over to TypeScript. To check for incompatibilities, I’ve been running tsc in the project directory and whittling down the places where it didn’t like my code. I’ve resolved most of them, but:

  1. That’s currently giving me some errors about “possibly undefined” when I access properties/functions of this.$alexaSkill and this$googleAction. Is there something I need to replace those with, or is there something I need to do to the code so it knows the app should be inheriting those properties, or should I be compiling the TypeScript another way?

  2. There are a couple of complaints from inside Jovo. I presume the error isn’t the code, or others would have noticed, so it must be something about my environment or the use of tsc. Any advice?

node_modules/jovo-framework/dist/src/index.d.ts:17:16 - error TS2665: 
Invalid module name in augmentation. Module 'express' resolves to
an untyped module at 'C:/myProjectDirector/node_modules/express/index.js', 
which cannot be augmented.

17 declare module 'express' {
                  ~~~~~~~~~

node_modules/jovo-plugin-debugger/dist/src/JovoDebugger.d.ts:1:23 - 
error TS2688: Cannot find type definition file for 'socket.io-client'.

1 /// <reference types="socket.io-client" />
                        ~~~~~~~~~~~~~~~~

node_modules/jovo-plugin-debugger/dist/src/JovoDebugger.d.ts:37:14 - 
error TS2503: Cannot find namespace 'SocketIOClient'.

37     socket?: SocketIOClient.Socket;
                ~~~~~~~~~~~~~~
  1. The remaining case that’s probably my own fault is that I’m trying to use axios to issue an HTTP get, and while the code is working fine in Javascript, TS complains about it. The code is

     const axios=require('axios')
     axios.get(uriString)
    

and the error is

src/player.js:375:9 - error TS2339: Property 'get' does not exist on type 
'typeof import("C:/myProjectDirectory/node_modules/axios/index")'.

375   axios.get(uri)
            ~~~

(Apologies if the answers are obvious to someone more experienced; as I’ve said I’m trying to learn Javascript, Jovo, and now Typescript simultaneously and that leads to a certain amount of tripping over my own feet.)


#2
  1. this.$alexaSkill might be undefined in the case where a request from Google Assistant is incoming, and vice versa, so you can only access the platform that’s sending a request. You can solve the compiler warning with the Non-Null Assertion Operator, but be sure to check if it’s actually an Alexa skill:
if(this.isAlexaSkill()) {
  this.$alexaSkill!.addAplDirective(...);
}
  1. This seems like some dependencies/module augmentations might be out of date, does it happen on a fresh project?

#3

Regarding your third issue, try this:

import axios from 'axios';

or, if you want to use CommonJs, try

const axios = require('axios').default;

Haven’t tested this tho, so I’m not 100% percent sure this will fix your issue :sweat_smile:


#4

Non-Null Assertion Operator: Ah. That’s a TypeScript feature rather than a JavaScript feature, which may be why I hadn’t seen it before. So I can apply it as I start actually cutting over. Good enough…

import axios from 'axios' tells me import must be used within a module, and my player.js, being based on the Jovo podcast player example, isn’t in a module. So I need to look up how to add that bit of semantics. (The docs I’ve found so far suggest there are multiple module systems depending on what version of Javascript you’re using and which preprocessors you run it through; I’m confused, and guidance on how to make javascript files into Jovo and tsc-compatible Modules would be VERY much appreciated! Is there a TS version of the podcast player example that I could look at for comparison?)

Out-of-date dependencies: Tried creating a new Jovo3 project directory, copying my code in (carefully not copying node_modules), npm installing my additional dependencies and running npm update. No change to those three tsc error messages, unfortunately.


#5

This appears to have something to do with the fact that I’m trying to use the “allowJs: true” option in tsconfig.json, which is supposed to compile the .js files under .ts to do an early check for whether there are likely to be problems when cutting over. When I take a jovo3 new directory, copy in the ts*.json files from a typescript project (modified to turn on allowJs) and run TSC, I get the same set of complaints about express and socket.io-client in the node_modules packages. So apparently something in the node_modules isn’t happy running in this mixed-language mode.

If I actually rename the files of the hello-world sample to .ts, I instead get only a conflicting-declaration complaint for app. Renaming the const app in app.ts to const thisApp resolved that and I got a clean TSC run.

So I’ll try renaming my own project’s files from .js to .ts, officially trying to cut over, and see what happens.

Update: I had to do some juggling to get this started. What seemed to work (at least well enough for me to start debugging types) was creating a jovo3 new --typescript project, doing a bit of npm juggling to replace the ts-jest, jest, and @types/jest packages with newer versions that didn’t have level conflicts (hopefully that doesn’t break anything). I then copied my .js files in over the template, renaming them .ts, and began iterating through compile/patch cycles adapting the code to use explicit types. Lots of nitpicking, partly because I’m trying not to allow implicit any; after a solid day’s work I’m down to “only” 49 errors, of varying complexity.

(Down to 4. Need a interface – or at least the outline of one – for the source database’s JSON report, and need to figure out how to handle .then() and .catch() in Typescript without allowing implicit any.)


#6

OK, the code seems to go through tsc cleanly, and the one issue that compilation didn’t catch was solved by adding more !=null tests. (TS is clever enough to realize that after an if testing for null it can assume this (mytype|null) value is actually mytype in the else clauses, but the output JS was treating it as undefined so I had to explicitly retest. Oh well.)

The TS code seems to be running, modulo a few minor issues I introduced while trying to deal with timezone and with Alexa and Google’s insistence on interpreting date slots as “next” (which makes sense for setting schedules) rather than “last” (which I need for accessing archives/history).

JOVO ISSUES:

  1. “jovo3 run” does not seem to automatically re-run tsc if files in prod\ are older than those in src\. I need to either manually delete prod\ to trigger a tsc, or run tsc manually, before trying to build/deploy/run the jovo code. That’s a bit less user-friendly than it should be, and may not be obvious to novices.

  2. When trying to run this TS-based version through jovo3, . does open the debugger – but the debugger is not initializing properly; it comes up empty rather than knowing about my intents. I’m currently seeing the same problem the JS-based copy too (separate development directory, while I’m working on the migration). I’m not sure whether this is more of the fallout from yesterday’s AWS breakage, or an issue introduced in jovo3 when starting to push folks to jovo4, or something else entirely.

With luck, by next week I’ll officially be on the TypeScript version and be looking at moving my data structures to DominoDB. After that, it’ll be time for me to start thinking about Jovo4.


#7

Minor wishlist item: I know we can switch between having an Alexa skill talk to the webhook and my local machine, or a lambda I’ve loaded with my code, by editing project.js to contain either the endpoint: property for the former or the host: { lambda: {...}} property for the latter. I tend to keep both in the file and just comment out the one I’m not using.

But it’d be convenient to be able to make this decision without manually editing that file, perhaps by adding a --target=webhook option which suppressed host: and used the webhook. (I’d suggest making webhook the default unless --target says otherwise, but changing defaults is always a bit fraught.)


#8

Have you seen our staging feature? https://v3.jovo.tech/docs/staging#project-configuration-stages

In one terminal window, run npm run tscw (which uses the tsc --watch command), and in another window run jovo3 run --watch. This way it should listen for updates. In Jovo v4, we’ve added support for watch by default.


#9

Sounds good; tnx.

I set up a watch.bat which launches windows with the two watching commands; seems to be Doing The Right Thing.

I still find it just a bit surprising that in non-watch mode, jovo3 run will recognize that this is a Typescript project and run tsc if, and only if, the dist directory (tsc's output) doesn’t already exist. Seems to me that the safer default, if watch isn’t specified, would be to always assume that tsc might be needed – while tossing out a reminder to the user that they might want to take advantage of watch to speed up the development cycle. But I grant this is a matter of taste as much as anything else.


#10

Discovered the hard way: tsc --watch seems to be writing back into src. And while that wakes up the jovo3 watch, it’s apparently reading from my dist directory – where tsc normally outputs. So it doesn’t actually get the new code

I can change the configurations to make this work. Slightly disappointed that just adding the watch options changed this behavior, though; I’d have hoped watch and non-watch behaved the same except for triggering automagically.