Link runtime dependencies, for VSCode autocomplete - javascript

I can't get VSCode to link in-code function parameters to their dependencies (vars, functions, modules), and get autocomplete suggestions in code.
Is there a way, with JSDoc, to make VScode recognize what module corresponds to a certain parameter?
// const database = require('./data-access/mongodb-adapter.js')
/**
* Factory for our service
*
* QUESTION: Can JSDoc refer a module in another file?
* (and instruct VSCode to wire it)
*
* #param {module('./data-access/mongodb-adapter.js')} database The adapter to the app database
*/
function makeService(database) {
return {
find: query => database.find(query)
}
}
module.exports = makeService
In PHP, with PHPDoc, I would type-hint the variable $database, adding an annotation /* #var MongodbAdapter $database */ in the same scope.
VSCode makes it clickable (when holding cmd ⌘) and links me to the referenced module/file/function.
Is there a way to specify the same on factory/constructor dependencies? (without transpiling)

in some cases you can if you are you using js files you need to create a jsconfig.json at the root of your project
{
"compilerOptions": {
"target": "es5",
"lib": [
"dom",
"ES5",
"dom.iterable",
"esnext",
"ES2015",
"ES2016",
"ES2017",
"ES2018",
"ES2019",
"ES2020"
],
"allowJs": true,
"checkJs": true,
"module": "commonjs",
"moduleResolution": "node",
"resolveJsonModule": true,
"declaration": true,
"declarationDir": "./typings",
"pretty": true,
"sourceMap": true,
"type":[],
"jsx": "react",
"baseUrl": "./node_modules",
"paths": {
"*": [
"./#types/*/index.d.ts",
"./#types/*/types.d.ts",
"./*/index.d.ts",
"./*/types.d.ts",
"../types/*/index.d.ts"
]
},
"typeRoots": [
"./#types",
"../types",
"../typings"
]
},
"typeAcquisition": {
"enable": true
},
"include": [
"src",
"types"
],
"exclude": [
"node_modules"
]
}
a jsconfig.json is the same as a tsconfig.json so if there are some types from your dependencies that you would like to use you need to install them and add them into the "type" array.
the way of using it is with for example we want to use express first we add
$ npm i -D #types/express
at the "type":[] array from the jsconfig we add express
...
"type":["express"]
...
now on your code
/**
* #description
* your desc
* #type {import("express").RequestHandler} RequestHandler
* #param {import("express").Request} req request
* #param {import("express").Response} res response
* #author Ernesto Jara Olveda
* #copyright (C) 14
*/
export const renderLoginPage = (req, res) => {
res.setHeader("Content-Type", "text/html; charset=uft-8");
res.render("login.hbs", {
canonical: "http://localhost:3000",
theme: "theme-light",
lang: "es",
highlight: "highlight-red",
gradient: "body-default",
token: req.csrfToken(),
});
};
if by any means the code u want to reference is not a package but a file in your src folder you have a problem, it cannot be done that way. you need to create a *.d.ts for example
src
--controller
----user
------user.js
------user.d.ts
let`s imagine we want to reference some from. user.js then we need to create a user.d.ts file and add all them definitions of your user.js
you can use https://www.typescriptlang.org/play?#code to help u out but as you can see there is alot you need to. write down. I recommend you to instead of doing all that create you project in typescript instead
enter image description here

Related

Javascript - Function returning different result when imported from a package

I have a function that is a part of my utils package that I import in my other modules:
export function urlQueryParamParser(params: URLSearchParams) {
const output:any = {};
const searchParams = new URLSearchParams(params);
new Set([...searchParams.keys()])
.forEach(key => {
output[key] = searchParams.getAll(key).length > 1 ?
searchParams.getAll(key) :
searchParams.get(key)
});
return output;
}
I export it like this in my index file along with other modules:
export * from './utils/urlQueryParamParser'
When I import it to my module:
import { urlQueryParamParser } from '#mypackages/utils'
and use it urlQueryParamParser(params) I get:
{
"[object Iterator]": null
}
Where, if I just copy the function and use it as a part of the file where I am actually calling it, I get the right return result, something like this:
{
"filter": "all",
"query": "",
"range": "today",
"from": "2022-11-22",
"to": "2022-12-22",
"claims": [
"damaged",
"missing-article"
]
}
Why is the result different when I import this function as part of the other package from when I use it as a function in the file where I am calling it?
The module where I am importing the function has following typescript version:
"typescript": "^4.7.2",
And the tsconfig.json of that module looks like this:
{
"compilerOptions": {
"baseUrl": "./src",
"target": "ESNext",
"allowJs": false,
"allowSyntheticDefaultImports": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"esModuleInterop": true,
"module": "ESNext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"importHelpers": true,
"noEmit": true,
"jsx": "react-jsx",
"skipLibCheck": true
},
"include": ["src/**/*.ts", "src/**/*.tsx"],
"files": ["custom.d.ts"]
}
The typescript version of the package module is:
"typescript": "^4.7.4"
And the tsconfig.json looks like this:
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"outDir": "./dist/", // path to output directory
"baseUrl": "./src",
"target": "esnext",
"lib": ["dom", "dom.iterable", "esnext"],
"module": "ESNext",
"allowJs": false,
"allowSyntheticDefaultImports": true,
"forceConsistentCasingInFileNames": true,
"isolatedModules": true,
"importHelpers": true,
"jsx": "react",
"noEmit": false,
"composite": true,
"incremental": true,
"declarationMap": true,
"plugins": [{ "name": "typescript-plugin-css-modules" }]
},
"exclude": ["**/node_modules", "**/.*/", "dist", "build"],
"include": ["src/**/*.ts", "src/**/*.tsx"]
}
Following up on my comment above.
I am guessing that the reason for this is that you forgot to spread the keys in your array in the file located in the package. Instead, you add the iterator object as the sole child into the array.
I think that you wrote new Set([searchParams.keys()]) instead of new Set([...searchParams.keys()]) in your current or previous version, and this incorrect piece of code got compiled wrongly (maybe it is even cached wrongly somewhere).
That said, I have no idea why TypeScript did not explicitly warn you when you did so. When reproducing the issue, I had to include // #ts-nocheck in my file.
Unfortunately, the issue is quite hard to reproduce since the information you gave was too sparse to create a working project. In the future, I recommend creating a CodeSandbox that contains the issue.
If you're using TypeScript older than the 2.3.1 your TypeScript will not detect the URLSearchParams as an iterable object and then when you try to spread it it should not work then by javascript magic its making it return the [object Iterator] instead of raise an error.
Also your params should be a string (or URL['search'] if you want a better explanation of what is the params when reading your function) since passing the params as a string or the result of an other URLSearchParams return the same thing as the output.
As of why it works on some cases and other don't the only thing that I can think of is that the target version of your tsconfig.json and the typescript of your package.json does not match.
Or this #mypackages is using a different typescript version then the project that is using the function. If you can provide more information about the versions and environment that you're running the function it should be easier to solve the problem.
Shot in the dark here but, does it work if you replace your spread operator with Array.from?
export function urlQueryParamParser(params: URLSearchParams) {
const output:any = {};
const searchParams = new URLSearchParams(params);
new Set(Array.from(searchParams.keys()))
.forEach(key => {
output[key] = searchParams.getAll(key).length > 1 ?
searchParams.getAll(key) :
searchParams.get(key)
});
return output;
}
In your module's tsconfig.json you have set noEmit to false, surely this should be set to true if you want it to actually compile.
Try to import it using its relative path.
import { urlQueryParamParser } from '../mypackages/utils'; // instead of '#packages/utils'
Or update the utils/index.js to:
import { urlQueryParamParser } from "./urlQueryParamParser";
export default {
urlQueryParamParser
};

Is there a way to find only unused JSDoc type definitions?

For example:
/**
* A number, or a string containing a number.
* #typedef {(number|string)} NumberLike
*
* Dummy type
* #typedef {(string|null)} StringLike
*/
/**
* Set the magic number.
* #param {NumberLike} x - The magic number.
*/
function setMagicNumber(x) {
}
As you can see NumberLike is used, but the StringLike type is not used and I'd like to find all such unused type definitions on our project. It's a Nodejs project with typescript installed.
Here's our tsconfig.json file:
{
"compilerOptions": {
"baseUrl": ".",
"jsx": "react",
"allowJs": true,
"checkJs": true,
"target": "ESNext",
"noEmit": true,
"moduleResolution": "node",
"isolatedModules": true,
"esModuleInterop": true,
"resolveJsonModule": true,
"strictNullChecks": true,
},
}
I'm not presently familiar with AST tools (which I think would be the domain of tools for finding this information). However, here's an example to illustrate an idea:
Let's say that the example JavaScript file in your post is saved at ./module.mjs. Here's another module, ./find-typedefs.mjs:
import {promises as fs} from 'fs';
function getTypedefNames (sourceText) {
const regex = /#typedef\s+{[^}]+}\s+[A-Za-z]+/g;
return [...new Set((sourceText.match(regex) ?? [])
.map(str => str.split(/\s+/).slice(-1)[0]))];
}
async function main () {
const [filePath] = process.argv.slice(2)
const text = await fs.readFile(filePath, {encoding: 'utf8'});
const names = getTypedefNames(text);
console.log(names.join('\n'));
}
main();
Running the file outputs this:
$ node find-typedefs.mjs module.mjs
NumberLike
StringLike
By using the function getTypedefNames as a basis for potentially more abstractions, you could automate searching the files in your project to produce a list of names that you can then search (and, for example, count) in each file. If you are using an editor like VS Code, you can also use regular expressions (like the one in that function) directly in the editor's Find menu.
Hopefully this will save you some manual work of eye-scanning.

Why typescript recognizes imported variables as of type any?

I have the following 2 files:
File src/helpers.ts:
const someHelperArray = ['value1', 'value2'] as const
module.exports = {
someHelperArray
}
File src/car.ts:
const {
someHelperArray
} = require('./helpers')
In the file car.ts when I hover over the someHelperArray I get this typescript type resolution: const someHelperArray: any instead of literal type I expected ('value1' | 'value2'). Essentially typescript doesn't recognize the types of imported variables from another file. I tried changing tsconfig.json settings but nothing helped. How to can I get typescript to recognize types imported from other files?
This is my tsconfig.ts:
{
"compilerOptions": {
"lib": ["dom", "es6", "scripthost", "esnext"],
"moduleResolution": "node",
"baseUrl": "src",
"watch": true,
"allowJs": true,
"esModuleInterop": true,
"module": "commonjs",
"sourceMap": true,
"inlineSources": true,
"allowSyntheticDefaultImports": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noImplicitAny": true,
"strictNullChecks": true,
"resolveJsonModule": true,
"experimentalDecorators": true
},
"exclude": ["node_modules", "**/*.spec.ts", "ts-out/**/*", "babel-out/**/*"]
}
A CommonJS module (with require) is not statically analyzable, which means the content of module is unknown before run time. Indeed, you could write any kind of dynamic code to assign it (i.e: Object.assign). So if you need to retain the types between modules you have to write them as ES6 modules because these are statically analyzable. Note that it is supported in latest versions of Node.js only.
If you wanted to keep using CommonJS modules in the source code, you could also have one file src/helpers.js:
const someHelperArray = ['value1', 'value2'];
module.exports = {
someHelperArray
}
And one src/helpers.d.ts like this:
export const someHelperArray: ['value1', 'value2'];

Data URI imports in typescript

In plain JS we can use import statements with data uri, e.g.:
import { number, fn } from 'data:text/javascript;charset=utf-8;base64,ZXhwb3J0IGNvbnN0IG51bWJlciA9IDQyOwpleHBvcnQgY29uc3QgZm4gPSAoKSA9PiAiSGVsbG8gd29ybGQiOw==';
Or dynamically:
import('data:text/javascript;charset=utf-8;base64,ZXhwb3J0IGNvbnN0IG51bWJlciA9IDQyOwpleHBvcnQgY29uc3QgZm4gPSAoKSA9PiAiSGVsbG8gd29ybGQiOw==')
.then(module => console.log(module));
However, putting the same code in a typescript file gets us a "Cannot find module" error.
My tsconfig.json is as follows:
{
"compilerOptions": {
"module": "esnext",
"lib": [
"esnext",
"es6",
"dom"
],
"moduleResolution": "node",
"outDir": "./build",
"noImplicitAny": false,
"removeComments": true,
"preserveConstEnums": true,
"sourceMap": true,
"target": "es6",
"jsx": "react"
},
"include": [
"src/**/*", "../addon/build/background.js"
],
"exclude": [
"node_modules",
"**/*.spec.ts"
]
}
Typescript docs on module resolution have no mention of data:... from what I see. Is there a way to get this to work in typescript?
See Wildcard module declarations in the Typescript handbook.
You could do something like this:
// types.d.ts
declare module "data:text/javascript;*" {
export const number: number;
export function fn(): string;
}
// index.ts
/// <reference path="types.d.ts"/>
import { number, fn } from 'data:text/javascript;charset=utf-8;base64,ZXhwb3J0IGNvbnN0IG51bWJlciA9IDQyOwpleHBvcnQgY29uc3QgZm4gPSAoKSA9PiAiSGVsbG8gd29ybGQiOw==';
This tells Typescript that an import matching the data:text/javascript; prefix will expose a number property (as a number type) and an fn property (which returns a string). Adjust the module declaration to fit your importer semantics as appropriate.

how to properly import jquery-ui in typescript file

i have included jquery like this
import $ = require('jquery');
and i am trying to make some div draggable like this.
constructor() {
$("#mydiv-id").draggable();
}
but i get error that draggable is not a function. i do have jqueryui.d.ts but how to import it properly ?
require.config is like
var require = {
paths: {
text: '../Scripts/libs/text',
jquery: '../Scripts/libs/jquery-1.11.2',
'jqueryui': '../Scripts/libs/jquery-ui',
jscroll: '../Scripts/libs/jquery.jscroll', // and others
and i don't see any mention of jquerUI in shim: section
You need to have it referenced from your project. Recommend using tsconfig for this which can form you compilation context https://github.com/TypeStrong/atom-typescript/blob/master/docs/tsconfig.md
Example
{
"compilerOptions": {
"target": "es5",
"module": "commonjs",
"declaration": false,
"noImplicitAny": false,
"removeComments": true,
"noLib": false
},
"filesGlob": [
"./**/*.ts",
"!./node_modules/**/*.ts"
],
"files": [
"./globals.ts",
"./linter.ts",
"./main/atom/atomUtils.ts",
"./main/atom/autoCompleteProvider.ts",
"./worker/messages.ts",
"./worker/parent.ts",
"./typings/jquery/jquery.d.ts",
]
}

Categories