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

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.

Related

Adding types["node"] in tsconfig results in type definition error

I have a NodeJS project that has a type declaration file so I can add properties to the Request object:
#types/express/index.d.ts:
import { User } from "../../src/entity/user.entity";
declare global {
namespace Express {
interface Request {
user?: User;
}
}
}
tsconfig.json:
"compilerOptions": {
"typeRoots": ["#types"],
...
}
So far, this works perfectly fine. However, when I want to add types or change typeRoots in my tsconfig.json to:
"compilerOptions": {
"typeRoots": ["./node_modules/#types", "#types"]
"types": ["node"]
...
}
it suddenly does not work anymore and throws errors at me in VSCode:
Property 'user' does not exist on type 'Request<ParamsDictionary, any, any, ParsedQs, Record<string, any>>'.ts(2339)
Any idea what causes this? Help is appreciated!
In ambient declaration files, you cannot have either import or export, because that would make them of type 'module'.
That means to use the typings defined in the declaration file, you would have to import or reference it in every file you need to use it in.
However, without the import or export, it's a script type file, and works as an ambient declaration file.
So how do we get around this:
import { User } from "../../src/entity/user.entity";
declare global {
namespace Express {
interface Request {
user?: User;
}
}
}
Well, it's a little known feature that you can use import like a function in a type:
declare global {
namespace Express {
interface Request {
user?: import("../../src/entity/user.entity").User;
}
}
}

How to export a TypeScript module so that can be imported both from TypeScript and Node?

Let's say a have the following TypeScript module:
function foo () {
return 123;
}
//TODO: Export code here
I want to export it in such a way that can be imported in these ways from TypeScript:
import foo from 'foo';
import * as foo from 'foo';
and in this way from Node:
const foo = require ( 'foo' );
Requirements:
I don't what the users of my module to have to set the allowSyntheticDefaultImports option
I want the code for exporting the module to be as clean as possible
I want to preserve type definitions
So far I've come up with the following "solutions", but they either don't preserve type definitions well enough or are too verbose:
export = foo['default'] = foo as typeof foo & { default: typeof foo };
export = foo['default'] = foo;
Is there a better way?
This is the best that I could come up with:
export = Object.assign ( foo, { default: foo } );
It's pretty terse, type definitions are properly generated, and it can be imported using all the aforementioned methods.
Since you are publishing as a commonjs package, you don't need rollup/webpack.
All you need is to transpile your code to es5 in commonjs using the TypeScript compiler.
Your tsconfig.json should look like this:
// tsconfig.json
{
"compilerOptions": {
"module": "commonjs",
"moduleResolution": "node",
"target": "es5",
"outDir": "dist" // customize this yourself.
...
}
}
And in your package.json:
// package.json
{
"main": "dist/index.js",
"typings": "dist/inde.d.ts",
"files": [
"dist" // customize this yourself.
],
...
}
Here is an example repository you can take a look into:
https://github.com/unional/path-equal
As far as I know export and imports is not supported in NodeJS yet.
Check this article: node-js-syntaxerror-unexpected-token-import
And as far as I know the only way to use TypeScript types syntax is only in a .ts file so an option is to compile your needed .ts files to .js using Babel.

Create a custom typings file

I just created a published npm package for the first time, called "Foo". I am trying to consume it in a typescript project, but none of the tutorials about how to declare modules with custom typings, are clear to me. Here are the key parts of the npm package:
news.ts
import { tdsRequest } from "../common/request";
function articles(uri) {
return tdsRequest({ method: "GET" }, uri).then(returnData => console.log(returnData, "return data"));
}
export {
articles,
};
main.ts (main export)
import * as news from "./services/news";
export default {
news
};
in the typescript project that's consuming the npm package:
import { news } from "Foo";
and in the typings file ( Foo.d.ts ) I did:
declare module "Foo" {
export {
news: Object,
};
}
I get the following errors: cannot find module news and Cannot export 'Object'. Only local declarations can be exported from a module.
You are mixing default and named exports.
You can do default export style -
main.ts:
import * as news from "./services/news";
export default {
news
};
ts project import:
import foo from "Foo";
const {news} = foo;
foo.d.ts:
declare module "Foo" {
export default {
news: Object,
};
}
Or you can do named exports:
main.ts:
import * as news from "./services/news";
export {
news
};
ts project import:
import {news} from "Foo";
foo.d.ts:
declare module "Foo" {
export const news: Object;
}
But more importantly, you should add declaration: true to your compilerOptions in tsconfig.json in your npm library.
This will generate the d.ts file for you and will save you lots of work. Then, you need to add in package.json a filed called types that will point to the main.d.ts file that will be generated for you. This will allow any project using your library + typescript to use the generated types automatically.

Declare interface in TypeScript .d.ts file and consume from JavaScript file

I'm using Visual Studio Code and would like to have intellisense hint when declaring my configuration variable as plain javascript object
jsconfig.json
{
"compilerOptions": {
"checkJs": true
},
"include": [
"src/**/*.js",
"types/**/*.d.ts"
],
"exclude": [
"node_modules"
]
}
types/index.d.ts
interface Foo {
a: string;
b: number;
}
declare var fooConfig: Foo;
src/app.js
const fooConfig = {
a: 'hello',
b: 123
}
I expect when declaring using const fooConfig VS Code might offer intellisense about a and b, the current result I got complaint message about re-declaring the variable fooConfig
P.S. I don't really know the possibility but I would like to have some intellisense so I can declare my configuration variable easily
Please guide
Thanks
You can use the JSDoc syntax to describe types in JS files:
src/app.js
/** #type {Foo} */
const fooConfig = {
a: 'hello',
b: 123
}
It works implicitly with d.ts files you have in your project, so there is no need to "include" them and there is no need to declare the var in the d.ts file.
Try const fooConfig: Foo = {...} in your JS file, after importing the interface. As it stands, TS is right you're declaring it twice.

DefinitelyTyped for systemjs-builder

Is there a DefinitelyTyped for systemjs-builder?
https://github.com/systemjs/builder
Because systemjs.d.ts does not seem to include it, and I get a "cannot find module" error when I try to import it:
import Builder = require('systemjs-builder');
And I couldn't find a systemjs-builder.d.ts file on the internet.
If there isn't a DefinitelyTyped for that library, how can I use it with TypeScript?
Any help will be profoundly appreciated!
This one https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/systemjs/systemjs.d.ts should includes Type definifions for the builder.
edit:
it seems like there are no typings avalible (maybe most use it with js). When you dont want to create the typings yourself you can put this in a systemjs-builder.d.ts:
declare module "systemjs-builder" {
var builder: any;
export = builder;
}
and write /// <reference path="../yourpath/systemjs-builder.d.ts" /> in your main typings.
To use in typescript you should import as follows:
import * as Builder from "systemjs-builder";
Then you can use
const builder = new Builder("", "systemjs.config.js");
builder.bundle("file.js", "output.js", {
format: "cjs",
minify: true,
sourceMaps: true,
mangle: false
});

Resources