How can I use packages that extend `koa.Request` in TypeScript? - node.js

I am trying to use koa-tree-router and koa-bodyparser at the same time, but I keep getting TypeScript errors:
export const userLoggingRouter = new KoaTreeRouter<any, DefaultContext>();
userLoggingRouter.post('/logs/action', (ctx) => {
const logEntries = ctx.request.body;
const user = ctx.state.user;
// ...
});
error TS2339: Property 'body' does not exist on type 'Request'.
I have #types/koa-bodyparser installed, and it contains the following definition:
import * as Koa from 'koa';
declare module 'koa' {
interface Request {
body: string | Record<string, unknown>;
rawBody: string;
}
}
But it doesn't seem to do anything. I found this question, but importing koa-bodyparser directly also does not do anything. How do I get TypeScript to recognize the extended Request type?
Edit: Creating a .d.ts file inside my project containing the following:
import {Request} from "koa";
declare module "koa" {
interface Request {
body: any;
}
}
Made the compile error go away, but this seems like an inelegant solution because I would have to copy over type information for every package that modifies koa.Request.

This was happening because I was using Yarn PnP and I had two different versions of #types/koa installed. Once I added a resolutions field to my package.json that forced all of the other TypeScript definitions to use the same version of #types/koa, everything worked.

Related

index.d.ts type definitions being ignored by tsc when compiling

I have an Express app built in TypeScript that I'm attempting to compile using the tsc CLI tool.
An issue that I'm facing, however, is that tsc seems to ignore the index.d.ts file that I've created and used to mutate the Express Request object.
This is my index.d.ts file:
declare global{
namespace Express{
export interface Request{
foo: string;
}
}
}
This allows me to do stuff like this inside my controller's requests without TypeScript spitting out a does not exist error:
export const example = async (req: Request) => {
const { foo } = req;
// Outputs "bar". This works completely fine in development.
console.log(foo);
};
I'm running the following command to build my app:
tsc ./Main.ts --outdir build
Which results in the following error multiple times across every controller that uses it:
error TS2339: Property 'foo' does not exist on type 'Request<ParamsDictionary, any, any, ParsedQs, Record<string, any>>'.
Try adding an empty export to your index.d.ts file:
export {};
declare global{
namespace Express{
export interface Request{
foo: string;
}
}
}

error TS2749: 'IRequestWithUser' refers to a value, but is being used as a type here. Did you mean 'typeof IRequestWithUser'?

I am extending Request in express library to contain a user property:
import { Request } from 'express';
export default interface RequestWithUser extends Request {
user: {
user_name: string;
password: string;
}
}
The title error appears in the first parameter annotation:
import IRequestWithUser from '../shared/interfaces/isRequestWithUser';
const router: Router = Router();
router.post('/myRoute', async (req: IRequestWithUser, res: Response) => {
/*
error TS2749: 'IRequestWithUser' refers to a value,
but is being used as a type here. Did you mean
'typeof IRequestWithUser'?
*/
I don't believe interfaces are values. They should purely be types. So what is causing this error?
Also tried
typeof IRequestWithUser This results in No overload matches this call
Perhaps there's something missing from your code snippets, but the only error I get on Typescript 3.9 is one that relates to the overload error that you allude to later in your question.
Because the .post() call is looking for a callback that expects type Request, you can't type your req argument as IRequestWithUser. This is because, although IRequestWithUser will contain all properties of Request, the Typescript compiler can't guarantee that the extra properties you plan to have in req will be there when the callback is run.
The recommended way to extend Express types is to use interface merging. This allows you to "redefine" the Request type altogether, but it's a global definition so it gets messy:
// definitions/express/index.d.ts
import * as express from 'express' // Unfortunately, you need some kind of import here to make this a valid module.
declare module "express-serve-static-core" {
export interface Request {
user: {
user_name: string;
password: string;
}
}
}
Your compiler should pick this file up on its own and support your new property using the original Request type. You might need to have an explicit typeRoots set in your tsconfig.json if you're using something like ts-node though.

Using Node-Geocoder with Typescript

I am trying to use Node-Geocoder in my typescript application, using the DefinitelyTyped type definitions found here. What I am wondering is how I am supposed to pass my configuration options to the Node-Geocoder instance. I would think you use it similar to how you use the library in Javascript, by passing the options to the constructor of the Geocoder object. However, that is giving me an error stating that the constructor does not take any arguments.
import DomainServiceInterface from "./../DomainServiceInterface";
import NodeGeocoder from "node-geocoder";
import LocationServiceInterface from "./LocationServiceInterface";
export default class LocationService implements DomainServiceInterface, LocationServiceInterface {
private geocoder: NodeGeocoder.Geocoder;
constructor() {
const options: NodeGeocoder.BaseOptions = {
provider: "google",
// other options
};
this.geocoder = new NodeGeocoder.Geocoder(options);
}
// other methods here
}
I attempted to look this up. However, all tutorials and content I could find related to Node-Geocoder are in Javascript.
What am I doing wrong?
Fair warning, I've never used the node-geocoder package.
If you look at the type definitions, the export is a function, not a class. (Technically declaration merging is used to export a function and a namespace.) That function takes Options and instantiates a Geocoder instance.
That said, you would create a Geocoder like this.
import * as NodeGeocoder from 'node-geocoder';
const options: NodeGeocoder.Options = {
provider: "google",
};
const geoCoder = NodeGeocoder(options);

Requested module does not provide an export?

First post here, be gentle :)
I'm in the process of learning a bit of node.js and I'm trying to do some stuff with a module called 'node-bom'
I am familiar with the 'require' method for calling node modules. However the author has provided the following code example, which doesn't work with node.js:
import {Bom} from 'node-bom'
const bom = new Bom({
/// options
})
bom.getParsedStateData('VIC', {
bypassCache: false // default: false to use cache
})
.then((stateData) => {
console.log(stateData)
/*
{
forecast: {}, // forecast data
observations: {} // current station's observations data
}
*/
})
This code produces the following error:
(node:10363) ExperimentalWarning: The ESM module loader is experimental.
file:///Users/ajudge/node-bom-testing/index.mjs:1
import {Bom} from 'node-bom'
^^^
SyntaxError: The requested module 'node-bom' does not provide an export named 'Bom'
at ModuleJob._instantiate (internal/modules/esm/module_job.js:93:21)
at async ModuleJob.run (internal/modules/esm/module_job.js:108:20)
at async Loader.import (internal/modules/esm/loader.js:128:24)
I've done some further reading and this appears to be an ES6 issue. So, I have changing the file to a .mjs and using the --experimental-modules flag, but I'm still not having any luck.
Can anyone clarify what I need to modify to get this module to work?
Thanks!
Change your filename to .js
The example in the package docs is more TypeScript oriented (hence the import).
In Node.JS your code should look like:
const Bom = require('node-bom')
const bom = new Bom.Bom({
/// options
})
bom.getParsedStateData('VIC', {
bypassCache: false // default: false to use cache
})
.then((stateData) => {
console.log(stateData)
/*
{
forecast: {}, // forecast data
observations: {} // current station's observations data
}
*/
})

graphql-tag/index has no exported member 'gql'

I'm converting my Angular app REST backend to GraphQL. I'm trying to import gql from graphql-tag. I'm searching and searching and my import looks like everyone elses...
import { Angular2Apollo } from 'angular2-apollo';
import { ApolloClient } from 'apollo-client';
import { gql } from 'graphql-tag';
But the gql has the red underline indicating not found. When i run ng serve I get this error in cmder...
... /node_modules/graphql-tag/index"' has no exported member 'gql'.)
I have run many, many apollo and angular npm installs, including npm install --save graphql-tag, trying install whatever I'm missing, doesn't seem to matter what I install.
What am I doing wrong?
Use the default export instead:
import gql from 'graphql-tag';
I see it. What is happening here is your code is trying to destructure gql off of the object that is exported out of graphql-tag, but the error is telling you there is no exported member of this name, meaning the exported object doesn't have a method of that name, or there are more than one object exported.
If you were to look in the code for graphql-tag, you would see it probably has a few export objects or it only has one that doesnt have a method called gql, so what you need to do is take gql directly, ie: without destructuring it, ie: without the { }.
This will be correct: import gql from 'graphql-tag'
You can see this all the time depending how you export and import things from modules.
Commit to memory that every time you see { something }, it is pulling something off an object.
Here is some sample code to illustrate:
const object = {
test: { name = 'Locohost' }
}
const { name } = object.test
console.log(name)

Resources