extend express request object with sequelize model using typescript 2 - node.js

I have some models, such as:
import * as Sequelize from 'sequelize'; // !
export interface UserAttributes {
id?: number;
email?: string;
googleToken?: string;
}
export interface UserInstance extends Sequelize.Instance<UserInstance, UserAttributes>, UserAttributes {
}
export interface UserModel extends Sequelize.Model<UserInstance, UserAttributes> {
}
// ... model definition
I need use import statement to use generic type Sequelize.Instance<TInstance, TAttributes>
I wright some middleware to add user instance to request object (or req.session object, doesn't metter)
Then I need typescript to know my new extending, for this I have file src/_extra.ts with content:
/// <reference path="./models/db.ts" /> -- also does not work
declare namespace Express {
export interface Request {
oauthClient: Googleapis.auth.OAuth2; // Googleapis is namespace
user?: UserInstance; // or ContactsProject.Models.UserInstance;
}
export interface Session {/* ... some same */}
}
I need to import my UserInstance type from my model file. Imports break all my _extra namespace definitions, inside namespace I can't use imports.
I have tried create namespace ContactsProject, but I getting same problem: if I use imports, typescript don't see my namespace, if I don't, I cant define my UserInstance type.
Sequelize doesn't declare namespaces.
I just want import some generic type in my namespace.
I use typescript Version 2.0.3.

When you use an import statement in a file, TypeScript treats it as a module. Therefor any namespace you declare inside it will not be global anymore, but local to that module.
Import your interface, and then wrap the namespace declaration in a declare global { ... }.

Related

Cannot find module when using type from another module in class-validator

I'm using typescript on both frontend and backend, so I wanted to create a "shared types" package for them. For the backend I'm using nest.js and I recently ran into an issue with the class-validator package.
In my shared types package I created the following enum-like type (since enums itself don't seem to be working if they are being used from a node module):
export const MealTypes = {
BREAKFAST: 'Breakfast',
LUNCH: 'Lunch',
DINNER: 'Dinner',
SNACK: 'Snack'
} as const;
export type ObjectValues<T> = T[keyof T];
export type MealType = ObjectValues<typeof MealTypes>;
I've installed the module locally using npm i and I'm able to import the type in my backend like this:
import { MealType, MealTypes } from '#r3xc1/shared-types';
Since I am not able to use this constant for the IsEnum class validator, I wrote my own:
#ValidatorConstraint({ name: 'CheckEnum', async: false })
export class CheckEnumValidator implements ValidatorConstraintInterface {
validate(value: string | number, validationArguments: ValidationArguments) {
return Object.values(validationArguments.constraints[0]).includes(value);
}
defaultMessage(args: ValidationArguments) {
return `Must be of type XYZ`;
}
}
and then I'm using it in a DTO class like this:
export class CreateMealDTO {
#Validate(CheckEnumValidator, [MealTypes])
#IsNotEmpty()
meal_type: MealType;
}
But as soon as I add the #Validate(...) I get the following error on start:
Error: Cannot find module '#r3xc1/shared-types'
It only does this, if I am passing a type that has been imported from a node module into a validator. It also happens with other validators like IsEnum.
I'm not really sure why this error is happening and I appreciate any hints or help!

Extending class from external 3rd party typescript module

Hello I have a problem with overwriting types
I want overwrite a type from a libary that adds a property to an other library's typings, the line is: https://github.com/discord-akairo/discord-akairo/blob/e092ce4e0c9e749418601476bcd054a30a262785/src/index.d.ts#L14
and in my code I declare it like this:
declare module 'discord.js' {
export interface Message {
util?: KopekUtil;
}
}
KopekUtil is extending the CommandUtil and the error i get is:
TS2717: Subsequent property declarations must have the same type. Property 'util' must be of type 'CommandUtil', but here has type 'KopekUtil'. index.d.ts(16, 13): 'util' was also declared here.
You mentioned trying to extend Command util class like so
export class KopekUtil extends CommandUtil{
constructor(handler, message: Message | CommandInteraction) {
super(handler, <Message>message);
}
send(options:string | MessageOptions , reply? : boolean){
//your logic
}
}
But I'm afraid it's not possible to overwrite a class that comes from external typescript module.
Although you can introduce a new method in class from external module or extend one of existing methods using object protoype.
The right way to do that
util.js
declare module 'discord-akairo'{
export interface CommandUtil {
mySendMethod(options:string | MessageOptions , reply?:any) : boolean
}
};
CommandUtil.prototype.mySendMethod = function (options:string | MessageOptions , reply?:any) : boolean{
return true;
}
Typescript now merges interfaces and you can use your extension
const message = new Message(new Client(),{},new TextChannel(new Guild(new Client(),{})))
message.util.mySendMethod("hello")

Type an untyped package with types from another package

Is it possible to re-export an existing typed module and use its types for an untyped module?
I am importing a library react-final-form-html5-validation that is just a tiny wrapper around typed react-final-form.
Are there any options for this?
I have tried many many many things ......., but this looks most promising, however, react-final-form does not declare a namespace and so I cannot use its types like I can use the React below.
project/index.d.ts
/// <reference types="react-final-form" />
//// <reference types="./node_modules/react-final-form/dist/index.d.ts" />
//// <reference path="./node_modules/react-final-form/dist/index.d.ts" />
//// <amd-dependency path="./node_modules/react-final-form/dist/index.d.ts" />
// importing resolves the types, but also turns it into a module, which in turn breaks it
// import * as RFF from react-final-form;
// import { FieldProps } from react-final-form;
// export * from react-final-form;
declare module 'react-final-form-html5-validation' {
var Field: React.ComponentType<FieldProps>
}
project/src/index.tsx
import { Field, Abc } from 'react-final-form-html5-validation'
// correct: Module '"react-final-form-html5-validation"' has no exported member 'Abc'
// later in App.render() { return ( .....
<Field />
// Field is of type React.ComponentType<any>
I also ran into the same problem, and I'm not very experienced in typescript so I'm not sure if this is the correct method of handling this very well, but I think I have a solution that seems to be working.
I implemented the types to match the ones from flow that were provided in the react-final-form-html5-validation files, which was basically FieldProps, but with some extra props to account for the error messages. I've only tested a little bit, but it seems to be working at the minute. As I said though, I'm fairly new to typescript and have only a basic understanding.
declare module 'react-final-form-html5-validation' {
import { ComponentType } from 'react';
import { FieldProps } from 'react-final-form';
type Messages = {
badInput?: string;
patternMismatch?: string;
rangeOverflow?: string;
rangeUnderflow?: string;
stepMismatch?: string;
tooLong?: string;
tooShort?: string;
typeMismatch?: string;
valueMissing?: string;
};
export type Html5ValidationFieldProps = FieldProps<any> & Messages;
export const Field: ComponentType<Html5ValidationFieldProps>;
}
The <FieldProps<any> part was taken from the main react-final-form which seems to be how the Field element is exported from there too.

Extend Request interface of Express for old JS library that doesn't support typings yet

I need node-vbauth in a TypeScript project. Sadly, there are no types yet. So when I want to access req.vbuser I got an error that his property doesn't exist. To fix this, I want to provide the missing type in a custom.d.tsfile:
export namespace Express {
export interface Request {
vbuser?: VbSessionInfo
}
}
export interface VbSessionInfo {
userid: number;
username: string;
usergroupid:number;
membergroupids:Array<string>;
email:string;
posts:number;
subscriptionexpirydate: number;
subscriptionstatus:number;
}
But this doesn't work. Typescript stil says the property vbuseris missing. I tried to explicitly include it in my tsconfig.json, no difference.
Found out that I'm missing to set my declaration in the global namespace and import the original Type which is going to be extended:
import {Request} from 'express';
declare global {
export namespace Express {
export interface Request {
vbuser: VbSessionInfo
}
}
export interface VbSessionInfo {
userid: number;
username: string;
usergroupid:number;
membergroupids:Array<string>;
email:string;
posts:number;
subscriptionexpirydate: number;
subscriptionstatus:number;
}
}
This works perfect, including intellisense in VS Code.

How to use TypeScript ambient declaration interfaces in interface declared in d.ts file

I want to do a helper in .ts file like:
class ResponseHelper implements IResponseHelper {...}
and IResponseHelper is simple .d.ts file with
import * as mongoose from 'mongoose';
import * as express from 'express'
interface IResponseHelper {
toStandartResponse(response: mongoose.Promise<any>, res: express.Response): mongoose.Promise<any>;
}
as you can see params in toStandartResponse are coming from mongoose which is ambiently declared. So this if fine but if i do so I cannot us it with 'implements' like class ResponseHelper implements IResponseHelper because I got errror 'Could not find symbol IResponseHelper in external module ResponseHelper' in other words compiler cannot see d.ts file.
If i remove import statements from d.ts. file everuthing is ok but i cannot specify response types then.
Is there any way to use it all together?
I believe when you use import in makes the file a module, so you must export any members you want visible:
export interface IResponseHelper { }
Now you can import it from other files:
import {IResponseHelper} from "./IResponseHelper";
class ResponseHelper implements IResponseHelper { }

Resources