Typescript declaration file for an external npm package - constructor - node.js

I am using ES6 and Typescript for my Node project, however one library is a commonjs library.
For that library, I created my own .d.ts declaration file:
module "#alpacahq/alpaca-trade-api" {
export interface AlpacaParams { ... }
// ...
export class Alpaca implements Broker {
// ...
constructor(params: AlpacaParams);
}
export default Alpaca;
}
Everything works as expected, but I'm having a problem with the constructor.
If I use that class from within my project, and I try this:
this.alpaca = new Alpaca.Alpaca({...});
I get told that Alpaca.Alpaca is not a constructor.
The only way it seems to work is if I do:
this.alpaca = new Alpaca.default({...});
I'm quite new to Typescript, so I'm sure I'm doing something wrong. Any ideas?
The latter works, so I'm not blocked in my work, but I would like to set things up properly.
Thank you!
Edited to show TS config and imports
tsconfig.json
{
"compilerOptions": {
"target": "es6",
"module": "es6",
"lib": ["es6", "es5"],
"sourceMap": true,
"outDir": "dist",
"rootDir": "src",
"strict": true,
"moduleResolution": "node",
"typeRoots": ["./types"],
"esModuleInterop": true,
"resolveJsonModule": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
}
}
This is how I import it. Couldn't figure out how to make it work otherwise. If I import modules ES6 style, it breaks unless I use commonjs. If I use commonjs, I get an "export undefined" error.
import * as Alpaca from '#alpacahq/alpaca-trade-api';

The problem is that you're importing all named exports with import * meaning that Alpaca refers to the module and .default refers to the exported class. Instead you should be importing just the default member.
Change your import to look like this:
// Import default member from module
import Alpaca from '#alpacahq/alpaca-trade-api';
this.alpaca = new Alpaca({...});

Related

TypeError [ERR_IMPORT_ASSERTION_TYPE_MISSING]: Module "file:///path/to/data.json" needs an import assertion of type "json"

I'm trying to import JSON in nodejs.
// tsconfig.json
...
"lib": ["es2022"],
"target": "es2022",
"module": "nodenext",
"moduleResolution": "node",
...
"resolveJsonModule": true,
...
// .swcrc.json
...
"target": "es2022",
...
"module": {
"type": "nodenext",
...
When I then compile it and run "start": "NODE_ENV=production node --es-module-specifier-resolution=node --experimental-json-modules --no-warnings lib/index.js" I get TypeError [ERR_IMPORT_ASSERTION_TYPE_MISSING]: Module "file:///path/to/data.json" needs an import assertion of type "json".
I then add:
import data from './data.json' assert {type: 'json'}
console.log(data)
I then open up the compiled code and I can see:
import data from"./data.json";console.log(data);
//# sourceMappingURL=index.js.map
At this point I thought maybe it's SWC not compiling the assertation?
I then run tsc --emitDeclarationsOnly and I get Import assertions are not allowed on statements that transpile to commonjs 'require' calls. At this point I have no idea why on earth commonjs has anything to do with it, I'm not using commonjs anywhere am I?
Also I'm using node 18.
What am I doing wrong? I am simply trying to import that json.
Edit: Okay so the reason TS was breaking was because of missing "include": ["src/**/*.ts", "src/**/*.json", "types.d.ts"],. After adding that it now works. Unfortunately SWC is still giving the same error so I cannot run it.
Finally figured it out. There's an experimental option in .swcrc.json that allows you to tell it to keep the assertations.
// .swcrc.json
...
"jsc": {
"experimental": {
"keepImportAssertions": true
}
}

Configure typescript to output imports with extensions, ex: `from './file.js``

Using node 14.x I would like to switch my project to full ES Modules, as it's now supported.
So I enabled on package.json "type": "module"
and my tsconfig.json looks like that:
{
"compilerOptions": {
"outDir": "dist", /* Redirect output structure to the directory. */
"strict": true, /* Enable all strict type-checking options. */
"esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
"skipLibCheck": true, /* Skip type checking of declaration files. */
"forceConsistentCasingInFileNames": true, /* Disallow inconsistently-cased references to the same file. */
"allowSyntheticDefaultImports": true,
"lib": ["ES2020"],
"module": "ESNext",
"moduleResolution": "node",
"target": "ES2020"
}
}
But the output files have unspecified extension in imports, it's instead REQUIRED for node 14.x to specify full file ext
For example:
import { ENV, redisConfig } from './config';
should instead be:
import { ENV, redisConfig } from './config.js';
TS won't handle that for you, but you can run Node with the node --experimental-specifier-resolution=node parameter.
Source
Unfortunately it seems that TS isn't going to support adding extension to the end of the import, since apparently it's not as simple as adding .js to the end of the import.

Nodejs Typescript type only package is not exporting all types properly

The package I'm building (https://github.com/plastikfan/xiberia/tree/develop) is a type only package (I'm not using DefinitelyTyped and this question is not about DT).
The package essentially is just a single file (index.ts) which contains various exported types such as:
export interface IYargsFailHandler {
(msg: string, err: Error, inst: yargs.Argv, command: any): yargs.Argv;
}
The problem is, when I use this in a client app, most of the types are missing and the only type that appears by intellisense is:
export const CoercivePrimitiveStrArray = ['boolean', 'number', 'symbol'];
All the other types are missing.
When I look at the corresponding index.js file, all it contains is:
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.CoercivePrimitiveStrArray = ['boolean', 'number', 'symbol'];
// -----------------------------------------------------------------------------
//# sourceMappingURL=index.js.map
The generated index.d.ts looks correct and contains all the types, (well there is one very weird definition that I can't account for at the end of the file):
export {};
My typescript config file is:
{
"compilerOptions": {
"allowJs": true,
"alwaysStrict": true,
"esModuleInterop": true,
"module": "commonjs",
"moduleResolution": "Node",
"noImplicitAny": true,
"sourceMap": true,
"strictNullChecks": true,
"target": "es5",
"declaration": true,
"declarationDir": "./dist",
"outDir": "./dist",
"diagnostics": true,
"lib": [
"es5",
"es2015",
"es6",
"dom"
],
"types": [
"node", "yargs"
],
},
"include": [
"./index.ts"
],
"exclude": [
"node_modules",
"dist"
]
}
So why are most of the types missing and how do I correct it, thanks.
EDIT: OOPS, I just made a really silly mistake. The types should not be in the resultant .js file. The only valid js is indeed the CoercivePrimitiveStrArrayCoercivePrimitiveStrArray which is being exported.
But that doesnt explain why the types that are being exported are not shown by intellisense on the clienbt side.
So on the client, this is what I have:
in a client file:
import * as xiberia from 'xiberia';
When I type, "xiberia.", I would expect to see all the types being exported, but I don't see any.
I read up on the triple slash directive and it appears they are not appropriate for this situation.
So what other config setting do i need for te intellisense to work as expected?
I fixed this problem by simplifying the package as much as possible. Previously, I was building artefacts into the 'dist' folder, a pattern which is widely used. I have removed this (this is a simple single file package, so using a dist folder is overkill) and simply build out the resultant index.d.ts, index.js and the .map files into the root directory. This also required explicity specifiying these files in the 'files' property in package.json (this ensures that these files are included in the resultant package tarball built when performing npm pack via the publish mechanism).
I don't understand why now intellisense is working as a result of these actions; perhaps somebody who knows better can comment.

Cannot find declaration file for 'autobind-decorator' in typescript

I am using Typescript and I want to import 'autobind-decorator' package inside project but ı stuck here.
I am getting this error line while compiling:
cannot find declaration file for 'autobind-decorator'. Implicitly has an 'any' type
I also tried #types/autobind-decorator npm package, but it didn't work.
Is there any option to get rid of this compile error ?
Here is my tsconfig:
{
"version": "2.1.5",
"compilerOptions": {
"module": "commonjs",
"lib": ["es2015", "es2016", "dom"],
"sourceMap": true,
"noImplicitAny": true,
"target": "es6",
"jsx": "react",
"skipLibCheck": true,
"experimentalDecorators": true
},
"include": [
"./packages/ld-web/src/**/*"
],
"exclude": [
"**/node_modules",
"**/*.d.ts"
]
}
import :
import * as autobind from "autobind-decorator";
From the type declarations here it declares and exports as module,
You need to install the #types as:
npm install #types/autobind-decorator --save-dev
and import as:
import autobind = require("autobind-decorator");
This worked, not using import at all:
const { autobind } = require('autobind-decorator');
I still had errors at runtime, so I read the doc again
https://www.npmjs.com/package/autobind-decorator
and used
const { boundMethod } = require('autobind-decorator');
that works,
I tried again using import and it failed.

How to reference externally defined node modules with string identifiers with typescript

I'm writing a set of node helper modules in typescript. I'm having difficulty getting typescript to interpret type information for external node modules such as "fs" and "path".
Importantly, I want to separate my module into a bunch of Typescript files, with a class/interface per file. They file layout is like this:
ts/ISomeInterface1.ts
ts/ISomeInterface2.ts
ts/SomeClass1.ts
ts/SomeClass2.ts
A class instantiates one or more interfaces, and is written as follows:
///<reference path="IFileSystemHelpers.ts" />
var fs = require("fs");
namespace myNmspace {
export class SomeClass1 implements SomeInterface1 {
public someIFunction() {
//do work
}
}
}
I'm using gulp-typescript to install type declarations for NodeJs. And I use a tsconfig.json file to build and reference these external typings. Here's a snippet:
{
"version": "1.8.9",
"compilerOptions": {
"target": "es6",
"module": "commonjs",
"moduleResolution": "node",
"sourceMap": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"noImplicitAny": false,
"removeComments": true,
"noLib": false,
"preserveConstEnums": true,
"declaration": true,
"suppressImplicitAnyIndexErrors": true,
"out": "./outputfile.js"
},
"filesGlob": [
"./**/*.ts",
"!./node_modules/**/*.ts"
],
"files": [
"./typings/main.d.ts",
"./ts/ISomeInterface1.ts",
"./ts/ISomeInterface2.ts",
"./ts/SomeClass1.ts",
"./ts/SomeClass2.ts",
"./ts/exports.ts"
]
}
Then classes are exported in the exports.ts file:
declare var exports: any;
if (exports) {
exports.SomeClass1 = myNmspace.SomeClass1;
exports.SomeClass2 = myNmspace.SomeClass2;
}
Then comes my problem. How do I get type information for the "fs" module?
I can see the following in the node.d.ts file (see here)that typings has installed:
declare module "fs" {
import * as stream from "stream";
import * as events from "events";
...
}
How do I force Typescript to interpret my fs variable in the SomeClass1.ts file as strongly-typed? In other words, what do I write here:
var fs : ??? = require("fs");
Can anyone help?
As an aside, I've noticed that if I replace the var with an import keyword, I get correct type interpretation for the fs variable. However the terms which point to my interfaces break and I get a squiggly line under the implements ISomeInterface1. Changing the pattern to use imports breaks my file separation, and seems valid only if I want to create a single-file node module.
Use ES6 style imports
import * as fs from 'fs'
import * as path from 'path'
This will also import the definitions from the definition file.
The syntax var x = require('x') does not (however import x = require('x') does, to add to the confusion)

Resources