How can I get the keyof a custom type declaration for NodeJS ProcessEnv? - node.js

I have added a custom type declaration to index.d.ts file for processEnv as outlined below:
declare global {
namespace NodeJS {
interface ProcessEnv extends IProcessEnv {
SOME_VAR: string;
SOME_OTHER_VAR: string
}
}
I then have a function where I want to check that the string value for the property is on the ProcessEnv which I then attempted to use the keyof but it doesn't seem to validate the string as a valid key on ProcessEnv.
type EnvTypes = keyof NodeJS.ProcessEnv;
const someFunction = (envKey : EnvTypes) => {
...
};
someFunction('MY_VAR'); // This should show as red as its not on the interface
The intellisense works as expected showing the valid options but I am unable to validate the string passed as a valid key. How can this be achieved?

Related

How to modify `request.params.id` type with declaration merging in Typescript/Express?

I've successfully added currentUser to Express' Request interface:
declare global {
namespace Express {
export interface Request {
currentUser?: User;
}
}
}
However, I also want to specify that the Request's path param req.params.id (where req is of type Express.Request) is a Number and not a String (which is the default type of params from the Request interface). But I'm not sure how to do this as it is nested inside of the ParamsDictionary interface. Here's a snippet of what I tried unsuccessfully:
declare global {
namespace Express {
export interface ParamsDictionary extends Request {
id: number;
}
}
}
For reference, Request is defined like this in the default Express/Typescript config file:
interface Request<
P = core.ParamsDictionary,
ResBody = any,
ReqBody = any,
ReqQuery = core.Query,
Locals extends Record<string, any> = Record<string, any>
> extends core.Request<P, ResBody, ReqBody, ReqQuery, Locals> {}
This is why I've been trying to modify the ParamsDictionary, which I assume contains the path params that can be found in req.params of a middleware or route function.
I still get errors such as this one, which proves that Typescript still assumes that the id is a string (which I confirm it isn't. it is passed as number):

TypeScript: Decorating a derived class with typed decorator function

I am trying to build an "Entity Framework"-like ORM library for node.js and MongoDB with TypeScript.
With this library, the consumer will be able to define a Model class (ex. Person) that will extend a Base class, and the class decorator will add additional data to the Person class (for example, instances array, that will contain all the Model's instances, and collection_name that will be the MongoDB collection name for the model, etc.).
Code in TypeScript playground.
So my first step was creating a Base class:
class Base<T>
{
static collection_name: string
static instances: Base<any>[]
_id: string
}
So the user will be able to define his model like so:
#decorator<Person>({collection_name: 'people'})
class Person extends Base<Person>
{
#field name
#field address
...
}
then I created a decorator class to set the collection_name and instances properties on the Person class:
function decorator<T>(config: { collection_name: string }) {
return function <T extends Base<T>>(Class: IClass<T>) {
Class.collection_name = config.collection_name;
Class.instances = [];
return Class
}
}
the decorator function receives the user-generated Class, and I am trying to create an interface that will describe the type of such class. I called it IClass:
interface IClass<T>
{
new(): Base<T>
instances: Base<T>[];
collection_name: string
}
new is the constructor (that returns a Base instance)
instances and collection_name are static properties of Base<T> and are non-static here (I'm not sure about this, is this right?)
However, when trying to define the user Model I get the following error:
#decorator<Person>({collection_name: 'people'}) // <==== ERROR HERE ===
class Person extends Base<Person>
{
}
Error:(23, 2) TS2345: Argument of type 'typeof Person' is not
assignable to parameter of type 'IClass>'.
Property 'instances' is missing in type 'typeof Person' but
Type 'typeof Person' is missing the following properties from type
required in type 'IClass>'.
It seems like the typescript compiler is ignoring the static members inherited from Base when checking the type of typeof Person.
How can I define the type of the Class property of the decorator function ?
The problem as jcalz points out is that your decorator is accepting a Class of a type that already has the static properties instances and collection_name. You need to use two different interfaces, one which is a type that simply constructs instances of T with the new(): T signature, and another that extends this interface to include the static properties your decorator will add.
class Base<T> {
static _id = 0;
private _id: number;
constructor () {
this._id = Base._id++;
}
}
interface BaseConstructor<T extends Base<T>> {
_id: number;
new(): T;
}
interface DecoratedBaseConstructor<T extends Base<T>> extends BaseConstructor<T> {
instances: T[];
collection_name: string;
}
function decorator<T extends Base<T>>(config: { collection_name: string }) {
return (baseConstructor: BaseConstructor<T>): DecoratedBaseConstructor<T> => {
const decoratedBaseConstructor = baseConstructor as Partial<DecoratedBaseConstructor<T>>;
decoratedBaseConstructor.collection_name = config.collection_name;
decoratedBaseConstructor.instances = [];
return decoratedBaseConstructor as DecoratedBaseConstructor<T>;
};
}
#decorator<Person>({collection_name: 'people'})
class Person extends Base<Person> {
name: string;
constructor () {
super();
this.name = 'foo';
}
}
With this approach, all of Base's static members must be public. Any static members of Base initialized in the decorator should go in the DecoratedBaseConstructor, and any remaining static members not initialized in the decorator should go in the BaseConstructor instead.
I assume that you use the generic type T in the Base class somehow in your actual code, but if you don't, you should remove the generic type from the Base class and everything else will still work the same.
Check out the above snippet in this playground.

How to create class based on string

How to create a Class based on given String?
I tried this code but it doesn't work.
public create(className: string): ISports {
// className = "Baseball";
return new global[className]();
}
I keep getting this error.
Element implicitly has an 'any' type because type 'Global' has no
index signature.

How to force the type of a variable in typescript?

I'm trying to write some function that converts a mongoose User model to a string with bullet points:
// Simplified version so you get the idea
interface IUser {
name: string
}
function userDetails (user: IUser, keys: string[]): string {
return keys.map((k: string): string => {
return `- ${k} : ${user[k]}`
})
.join('\n')
}
But I'm having a strange compiler error, where user[k] is underlined:
Index signature of object type implicitly has an 'any' type.
Is there a way to "force" typescript to admin that user[k] is a string ?
I tried user[k] as string or <string> user[k] without success.
Also, if I remove the ${user[k]} from the returned string, then the compiler stop complaining
Appart from the compiler error, everything works at runtime.
Thanks !
Try this:
function userDetails(user: IUser, keys: string[]): string {
let dic: { [prop: string]: string } = <any>user
return keys.map((k: string): string => {
return `- ${k} : ${dic[k]}`
})
.join('\n')
}

Cannot extend base class with Mongoose and Typescript in NodeJS

I'm attempting to use Mongoose 4.5.4 and it's typings in NodeJS with Typescript (obviously) while utilizing the Repository Pattern.
RepositoryBase:
export class RepositoryBase<T extends mongoose.Document> implements IRead<T>, IWrite<T> {
private _model: mongoose.Model<mongoose.Document>;
constructor(schemaModel: mongoose.Model<mongoose.Document>) {
this._model = schemaModel;
}
create(item: T): mongoose.Promise<mongoose.model<T>> {
return this._model.create(item);
}
//......
//Additional CRUD methods excluded for brevity
}
UserRepository:
export class UserRepository extends RepositoryBase<IUserModel> {
constructor() {
super(UserSchema);
}
}
I have the following type error on the call to super() from UserRepository
[ts]
Argument of type '<U>(name: string) => IModelConstructor<U> & EventEmitter' is not assignable to parameter of type 'IModelConstructor<Document> & EventEmitter'.
Type '<U>(name: string) => IModelConstructor<U> & EventEmitter' is not assignable to type 'IModelConstructor<Document>'.
Property 'findById' is missing in type '<U>(name: string) => IModelConstructor<U> & EventEmitter'.
import UserSchema
Does anyone know why? My creation of my model (UserSchema) is very simple:
let model = mongooseConnection.model<IUserModel>("Users", UserSchema.schema);
I would much appreciate a push in the right direction.
Doing a simple export const model = mongooseConnection.model<IUserModel>("Users", UserSchema.schema); was enough to solve this typing issue.

Resources