VSCode does not recognize jest custom matcher - jestjs

I'm currently struggling with making a Jest custom matcher work in VSCode with typescript.
I wrote a custom matchers file like the following (I've simplified the test for brevity reasons):
export {}
declare global {
namespace jest {
interface Matchers<R, T = {}> {
toSucceed(): R
}
}
}
function toSucceed(this: jest.MatcherContext, received: Result<any>): any {
return {
pass: true,
message: () => 'Custom matcher message',
}
}
expect.extend({
toSucceed,
})
I included this file path in my jest.config.ts under the tag setupFilesAfterEnv.
Then I wrote tests like:
it('should pass', () => {
expect(foo()).toSucced()
})
All this configuration works fine, but I still get a VSCode inline error:
Property 'toSucceed' does not exist on type 'JestMatchers<any>'
JestMatchers is a type definition inside the #types/jest root, since it is a type I cannot augment it directly.
Have anyone experienced any similar problem?

I encountered a similar error message when the namespace declaration of "interface Matchers" was incorrect. After fixing that, a custom matcher appeared in jestMatchers, too. Matchers and jestMatchers are connected.
Your code works fine for me—except for the undefined Result<any>—so you can try to directly import in test and see if Visual Studio Code can understand the declaration:
import "./toSucceed";
If direct import works for you, I would guess that there's a problem with the jest extension which helps Visual Studio Code understand jest.config.

Did you remember to add the type definitions to your tsconfig?
For me it works fine when I put the type definition in a d.ts file at the root level, and add that path to the "include" option in my tsconfig:
{
// Some other config
"include": [
"custom-matchers.d.ts"
]
}
Haven't tested with your custom matcher specifically, but I had a similar issue that I resolved like this.
So your custom-matchers.d.ts would be:
export {}
declare global {
namespace jest {
interface Matchers<R, T = {}> {
toSucceed(): R
}
}
}

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!

Apply node module with object prototype methods

Good morning.
I'm trying to create a node module witch right now is published as redgem.
It consists of adding methods to base objects prototypes. For example:
const obj = { a: 'b', c: 'd' }
obj.map(do stuff)
However i haven't found a good way to apply these methods in a project. If i run the tests (they are inside the module) they all run smoothly since methods are added to base objects (arrays, strings and actual objects). But if i do the same thing in another project i get errors like Uncaught TypeError: Illegal invocation.
Could anyone please help me setup the package?
The way i'm trying to use redgem in projects right now is:
import redgem from 'redgem';
redgem():
Thanks.
So, basically this is how i export the module:
export default function apply () {
stringMethods.forEach((method) => {
String.prototype[method.name] = method.function
})
arrayMethods.forEach((method) => {
Array.prototype[method.name] = method.function
})
objectMethods.forEach((method) => {
Object.prototype[method.name] = method.function
})
}
Where every method is in the following form
{
// Tells wether the array is empty.
name: 'empty',
function: function () {
return this.length == 0
}
}

#mailchimp/mailchimp_marketing/types.d.ts' is not a module in nodeJs

I imported import #mailchimp/mailchim_marketing in my NodeJS app:
import mailchimp from "#mailchimp/mailchimp_marketing";
However, it gives following error:
type.d.ts is not a module
I have searched to see if there is a #types/#mailchimp/mailchimp_marketing but I couldn't see it.
The type.d.ts file provided by #mailchimp/mailchimp_marketing doesn't have the types of the library and the package doesn't have a #types package too. So it's necessary to create your own to override the provided by him.
To do this, creates the folders #types/#mailchimp/mailchimp_marketing (one inside another) and creates the file index.d.ts inside mailchimp_marketing.
This file has to contain the declaration of the module, and inside than, the functions and types what you gonna use from library. In my case:
declare module '#mailchimp/mailchimp_marketing' {
type Config = {
apiKey?: string,
accessToken?: string,
server?: string
}
type SetListMemberOptions = {
skipMergeValidation: boolean
}
export type SetListMemberBody = {
email_address: string,
status_if_new: 'subscribed' | 'unsubscribed' | 'cleaned' | 'pending' | 'transactional'
merge_fields?: {[key: string]: any}
}
export default {
setConfig: (config: Config) => {},
lists: {
setListMember: (listId: string, subscriberHash: string, body: SetListMemberBody, opts?: SetListMemberOptions): Promise<void> => {}
}
}
}
SetListMemberBody has much more fields and setListMember is not void, but i added just what I gonna use. To discover this fields and functions I looked in the source code (https://github.com/mailchimp/mailchimp-marketing-node) and api documentation (https://mailchimp.com/developer/api/marketing/list-members/add-or-update-list-member/).
In my case (Typescript 3.7.3) was not necessary to change tsconfig.json, but if you use older version maybe is necessary to add "typeRoots" for your compilerOptions in tsconfig.json:
"compilerOptions": {
"typeRoots": ["#types", "node_modules/#types"]`
// other options
}
After all this, I used the library normally:
import mailchimp, { SetListMemberBody } from '#mailchimp/mailchimp_marketing'
import crypto from 'crypto'
mailchimp.setConfig({
apiKey: process.env.MAILCHIMP_KEY,
server: 'serverHere',
});
const listId = 'listIdHere'
export const addSubscriber = async (member: SetListMemberBody): Promise<void> => {
const hash = crypto.createHash('md5').update(member.email_address).digest('hex')
await mailchimp.lists.setListMember(listId, hash, member)
}
replace your code to this:
const mailchimp = require("#mailchimp/mailchimp_marketing");
Right, you wont have type safe but at least your code will work.
Types for #mailchimp/mailchimp_marketing are available meanwhile.
Use
yarn add -D #types/mailchimp__mailchimp_marketing
or
npm install --save-dev #types/mailchimp__mailchimp_marketing
to install the types package.
EDIT: the types do not seem to be complete.
With a similar issue with #mailchimp/mailchimp_transactional I had to create my own mailchimp__mailchimp_transactional.d.ts with declare module (this package also has just types.d.ts and it is almost empty unlike the types.d.ts in mailchimp_marketing package).
So you can type to create your own type description file using their types.d.ts, place it in #types folder of your project and add #types to tsconfig.json like this:
"compilerOptions": {
// other options here
"typeRoots": [
"#types",
"node_modules/#types"
]
mailchimp__mailchimp_transactional.d.ts
/* eslint-disable camelcase */
declare module '#mailchimp/mailchimp_transactional' {
...
}
A quick & dirty solution is to delete the types.d.ts file, which prevents the error, though you will no longer get any type information for the API.

Puppet - Set defined types in Nodes.pp

How to overwrite defined type in nodes.pp? I want to able to set custom domain using nodes.pp. Case Default isn't an option.
I'm using puppet 6.0..
The following method doesn't work. It says Could not find declared class resolv::resolv_config.
It looks like it used to work in 3.0 according to this answer.
nodes.pp
node "test001" {
class { 'resolv::resolv_config':
domain => "something.local",
}
}
modules/resolv/manifests/init.pp
class resolv {
case $hostname {
/^[Abc][Xyz]/: {
resolv:resolv_config { 'US':
domain => "mydomain.local",
}
}
}
}
define resolv::resolv_config($domain){
file { '/etc/resolv.conf':
content => template("resolv/resolv.conf.erb"),
}
}
resolv.conf.erb
domain <%= #domain %>
There are a couple of problems here, but the one causing the "could not find declared class" error is that you are using the wrong syntax for declaring a defined type. Your code should be something like this:
node "test001" {
resolv::resolv_config { 'something.local':
domain => "something.local",
}
}
There are examples of declaring defined types in the documentation, https://puppet.com/docs/puppet/latest/lang_defined_types.html.
Once you get that working, you'll find another problem, in that this definition
define resolv::resolv_config($domain){
file { '/etc/resolv.conf':
content => template("resolv/resolv.conf.erb"),
}
}
will cause a error if you try to declare more than one resolv::resolv_config, because they will both try to declare the /etc/resolv.conf file resource. You almost certainly wanted to use a file_line resource.

TypeScript and Node and importing

I'm trying to use TypeScript inside Node.js with typescript-require. So I init it like:
// index.js, line 1.
require("typescript-require")({ "nodeLib": true });
And so I load the Main.ts file. Like it:
// index.js, line 2.
require("Main.ts").init();
So I have a Dictionary interface:
// Main.ts, line 8.
interface Dictionary<TValue> {
[index: string]: TValue;
}
When I put this code directly on Main.ts it run fine, like it:
// Main.ts, line 12.
var list: Dictionary<number> = {};
But I like to separate the Dictionary from Main. To allow others files use this interface without duplicate it. And here starts my problem. I don't know how I can do that. I tried a lot things, like use reference and import require, and I receive errors from all methods.
/// Without any import, I get an obvious error; or,
/// <reference path="Dictionary" />; or,
/// <reference path="Dictionary.ts" />; or,
/// <reference path="./Dictionary.ts" />; or,
import Dictionary = require("Dictionary");
>> Main.ts(6,18): error TS2304: Cannot find name 'Dictionary'.
import Dictionary = require("Dictionary.ts");
>> Main.ts(1,29): error TS2304: Cannot find name 'Dictionary'.
>> Main.ts(6,18): error TS2315: Type 'any' is not generic.
In all cases, seems that Main.ts doesn't include the file correctly, so I can't reuse that. So I'm forgetting something?
Before I post it I found the solution. I was trying export interface on file Dictionary.ts like it:
export interface ... { ... }
But I need export = it. Like:
interface Name { ... }
export = Name;

Resources