I am using VS Code 1.2.1 and learning how the rich editing support works for javascript. (intellisense, peek, go to definition, etc)
There are two situations where vscode does successfully load the require()-ed module, but one situation that it does not provide any rich editing support. Here is an example w/ comments:
// vscode knows about var _ because I already did
// $ typings install lodash
var _ = require('lodash');
// vscode knows about var fu, because the test.js is in project context.
var fu = require('./test.js');
// vscode is unaware of var tree, even though I copied the src into the
// project context.
// $ cp -r node_modules/tnt.tree/src lib/tnt.tree
var tree = require('tnt.tree');
console.log(tree); // ok
The last one, tnt.tree is giving me trouble. The code above does successfully build with webpack, and run OK. But vscode says the variable tree is 'any', with no additional info.
Finally, here is my jsconfig.json:
{
"compilerOptions": {
"target": "ES6",
"module": "commonjs",
"diagnostics": true,
"allowSyntheticDefaultImports": true
},
"exclude": [
"node_modules",
"dist"
]
}
Summary: there are no typings for tnt.tree. So I copied the module of interest (tnt.tree) out of node_modules and into a lib/ directory, to try to make vscode aware of it in the project context. But that doesn't seem to work. Any pointers would be greatly appreciated, as I am sure this is a problem I will revisit over and over when trying to learn new Js modules.
I filed a bug with the vscode team, and they reassigned it to the typescript team. So this sounds like a bug with the editor itself:
IntelliSense based on type inference not working in node_modules?
https://github.com/Microsoft/TypeScript/issues/9323
Related
Let's say I have two projects with following file structure
/my-projects/
/project-a/
lib.ts
app.ts
tsconfig.json
/project-b/
app.ts // import '../project-a/lib.ts'
tsconfig.json
I want to consume lib.ts located in project-a also from project-b. How to do that?
Release it as NPM module - absolutely don't want that, it's an overkill for such a simple use case. I just
want to share one file between two projects.
Use import '../project-a/lib.ts' - doesn't work, TypeScript complains
'lib.ts' is not under 'rootDir'. 'rootDir' is expected to contain all source files.
Put tsconfig.json one level up so it would cover both project-a and project-b - can't do that, the TypeScript config is slightly different for those projects. Also it's not very convenient, don't want to do that.
Any other ways?
Since Typescript 3.0 this can be done with Project References.
Typescript docs: https://www.typescriptlang.org/docs/handbook/project-references.html
I believe you would have to move lib.ts into a small ts project called something like 'lib'
The lib project should have a tsconfig containing:
// lib/tsconfig.json
{
"compilerOptions": {
/* Truncated compiler options to list only relevant options */
"declaration": true,
"declarationMap": true,
"rootDir": ".",
"composite": true,
},
"references": [] // * Any project that is referenced must itself have a `references` array (which may be empty).
}
Then in both project-a and project-b add the reference to this lib project into your tsconfig
// project-a/ts-config.json
// project-b/ts-config.json
{
"compilerOptions": {
"target": "es5",
"module": "es2015",
"moduleResolution": "node"
// ...
},
"references": [
{
"path": "../lib",
// add 'prepend' if you want to include the referenced project in your output file
"prepend": true,
}
]
}
In the lib project. Create a file index.ts which should export all your code you want to share with other projects.
// lib/index.ts
export * from 'lib.ts';
Now, let's say lib/lib.ts looks like this:
// lib/lib.ts
export const log = (message: string) => console.log(message);
You can now import the log function from lib/lib.ts in both project-a and project-b
// project-a/app.ts
// project-b/app.ts
import { log } from '../lib';
log("This is a message");
Before your intelissense will work, you now need to build both your project-a and project-b using:
tsc -b
Which will first build your project references (lib in this case) and then build the current project (project-a or project-b).
The typescript compiler will not look at the actual typescript files from lib. Instead it will only use the typescript declaration files (*.d.ts) generated when building the lib project.
That's why your lib/tsconfig.json file must contain:
"declaration": true,
However, if you navigate to the definition of the log function in project-a/app.ts using F12 key in Visual Studio code, you'll be shown the correct typescript file.
At least, if you have correctly setup your lib/tsconfig.json with:
"declarationMap": true,
I've create a small github repo demonstrating this example of project references with typescript:
https://github.com/thdk/TS3-projects-references-example
This can be achieved with use of 'paths' property of 'CompilerOptions' in tsconfig.json
{
"compilerOptions": {
"paths": {
"#otherProject/*": [
"../otherProject/src/*"
]
}
},
}
Below is a screenshot of folder structure.
Below is content of tsconfig.json which references other ts-project
{
"compilerOptions": {
"baseUrl": "./",
"outDir": "./tsc-out",
"sourceMap": false,
"declaration": false,
"moduleResolution": "node",
"module": "es6",
"target": "es5",
"typeRoots": [
"node_modules/#types"
],
"lib": [
"es2017",
"dom"
],
"paths": {
"#src/*": [ "src/*" ],
"#qc/*": [
"../Pti.Web/ClientApp/src/app/*"
]
}
},
"exclude": [
"node_modules",
"dist",
"tsc-out"
]
}
Below is import statement to reference exports from other project.
import { IntegrationMessageState } from '#qc/shared/states/integration-message.state';
I think that #qqilihq is on the right track with their response - Although there are the noted potential problems with manually maintaining the contents of a node_modules directory.
I've had some luck managing this by using lerna (although there are a number of other similar tools out there, for example yarn workspaces seem to be somewhat similar, although I've not used them myself).
I'll just say upfront that this might be a little heavyweight for what you're talking about, but it does give your project a lot of flexibility to grow in the future.
With this pattern, your code would end up looking something like:
/my-projects/
/common-code/
lib.ts
tsconfig.json
package.json
/project-a/
app.ts (Can import common-code)
tsconfig.json
package.json (with a dependency on common-code)
/project-b/
app.ts (Can import common-code)
tsconfig.json
package.json (with a dependency on common-code)
The general theory here is that the tool creates symlinks between your internal libraries and the node_modules directories of their dependant packages.
The main pitfalls I've encountered doing this are
common-code has to have both a main and types property set in its package.json file
common-code has to be compiled before any of its dependencies can rely on it
common-code has to have declaration set to true in its tsconfig.json
My general experience with this has been pretty positive, as once you've got the basic idea understood, there's very little 'magic' in it, its simply a set of standard node packages that happen to share a directory.
Since I'm the only dev working on 2 projects which require some shared code folder I've setup a 2-way real time sync between the common code shared folders.
project A
- shared/ -> 2-way sync with project B
- abc/
project B
- shared/ -> 2-way sync with project A
- xyz/
It's a one-time quick setup but gives benefits like:
no hassle of configuring and managing mono-repo/multiple tsconfig
no build step to get latest code, works instantly
works with cloud build tools as shared code is inside repo instead of symlink
easier to debug locally as all files are within the project
I can further put checks on shared folder with commit hooks/husky and throw warnings etc.
And in-case if I want to collaborate with other team members, I can mention to use this sync tool as part of project setup.
It seems we've got a few options here:
(1) Put both projects in a mono repo and use answer given by #ThdK using TS project references
NOT GOOD if you don't want a mono repo
(2) Use Lerna - see answer by #metric_caution
NOT GOOD if you can't be bothered to learn Lerna / don't want to publish your shared files to npm
(3) Create a shared npm package
NOT GOOD if you don't want to publish your shared files to npm
(4) Put shared folders in a "shared" directory in project A and write a script to copy files in the shared folder from project A to project B's shared folder that is executed on a git push OR a script to sync the two folders.
Here the script can be executed manually when copying / syncing is required. The copying / syncing could also be done prior to a git push using husky and the shared files added to git automatically in the script.
Since I don't want a mono repo and I can't be bothered to publish an npm package for such a pathetic purpose I'm gonna go with option 4 myself.
In a similar scenario, where I also wanted to avoid the overhead of having to perform an NPM release, I went for the following structure (after lots of trial and error and failed attempts):
/my-projects/
/node_modules/
/my-lib/
lib.ts
tsconfig.json
package.json
/project-a/
app.ts
tsconfig.json
/project-b/
app.ts
tsconfig.json
The central idea is to move the shared stuff to a node_modules directory above the individual projects (this exploits NPMs loading mechanism, which would start looking for dependencies in the current directory and then move upwards).
So, project-a and project-b can now access the lib simply via import { Whatever } from 'my-lib'.
Notes:
In my case, my-lib is actually only for shared typings (i.e. .d.ts files within a lib subdirectory). This means, I do not explicitly need to build my-lib and my my-lib/package.json looks as follows:
{
"name": "my-types",
"version": "0.0.0",
"private": true,
"types": "lib/index"
}
In case my-lib contains runnable code, you’ll obviously need to build my-lib, so that .js files are generated, and add a "main" property to the package.json which exposes the main .js file.
Most important: Despite its name, /my-projects/node_modules only contains custom code, no installed dependencies (they are actually in the individual projects project-a/node_modules and project-b/node_modules). This means, there’s an explicit git ignore setting, which un-ignores the /node_modules directory from being committed.
Is this a clean solution? Probably not not. Did it solve my issue? Yes. Am I happy to hear about improvement suggestions? Absolutely!
I switched to deno. Code sharing in TypeScript projects is easy, at long last.
I have a TypeScript project with a few real TypeScript classes, not just functions. I have 1 file per class. There are classes that depend on other classes. I can compile these whole TypeScript to JavaScript with tsc. I can also run locally the JavaScript output with Node. It runs fine. Actually I run a small test class. I don't use any third party library.
I want that a browser can run the generated JavaScript. So I tried to include the whole JavaScript output in my HTML website.
But the JavaScript does not run in a browser. I just tried to create an instance from one of the generated JavaScript class. I also copied the needed require.js file to my HTML project.
My question is: How can I configure tsc and requirejs that a browser can run my generated JavaScript?
My tsc generated require calls with relative paths. It is not a mystery that this does not work in a browser, but I don't know which variant would work. I am a bit upset, because I read so many tutorials and tried so many combinations and I am still not wiser. Many tutorials mention webpack, gulp, baseUrl and such things without showing an example that would run in a browser.
My tsc config is:
{
"compilerOptions":
{
"module": "none",
"target": "ES2018",
"removeComments": true,
"rootDir": "Source",
"outDir": "Build"
},
"exclude": [
"node_modules"
]
}
My team works on a relatively large NodeJS project, written in ES6, transpiled by babel, and then deployed as AWS lambdas with Serverless. This project is focused around consuming, mapping/transforming, and outputting one specific object type, which we have defined.
Our problem is, ECMA/JavaScript is not strongly typed, so if we make a mistake like treating a field as an array somewhere and a string somewhere else, there's nothing to catch that except runtime errors. We have also poorly documented the structure of this object, so sometimes consumers send us instances of the object with data in slightly misnamed fields that we say we process, but don't actually use.
I am looking for a way to create some kind of schema or type definition for this specific object in our project, so we can use that to correct our code, make our processing more robust, and create much better documentation for it. Now, I know VSCode offers some basic type checking in JavaScript, but I don't think it's feasible to try to JSDoc a really big object and then put that doc in every file that uses the object. I have found that VSCode can also, somehow, drive that checking with .d.ts files but I don't understand if or how I can leverage that for a specific, custom object that we've designed. Most of what I have found seems to be specifically related to pulling .d.ts files for external libraries.
So, TL:DR, Is it possible, in a NodeJS/ES6 project, to make one object, widely used throughout that project, strongly typed? Error checking in VSCode would be acceptable, but some kind of command-line linting that we could trigger before transpiling would be great too.
Alright, figured it out. After I posted this question, I kept googling and after an hour or so hit this article on StrongLoop by Sequoia McDowell: https://strongloop.com/strongblog/type-hinting-in-javascript/
I followed it pretty closely and, using the "typings" package, I was able to initialize a "typings" folder at the root of my project. The contents of that folder now look like this:
typings/
├── models/
│ └── myObject.d.ts
└── index.d.ts
The content of that index.d.ts file look like this:
/// <reference path="models/myObject.d.ts" />
And the contents of that myObject.d.ts file look something vaguely like this:
declare namespace MyProject {
export interface MyObject {
aString?: string;
anotherString?: string;
aComplexType?: ComplexType;
}
interface ComplexType {
aString?: string;
anotherComplexType: SecondComplexType;
}
interface SecondComplexType {
aString?: string;
}
}
With that done, I had to start marking instances of this object with JSDoc. That doc mainly took two forms:
/**
* Creates an instance of UtilityThing.
* #param {MyProject.MyObject} myObject
*
* #memberof UtilityThing
*/
constructor(myObject) {
this.myObject = myObject;
}
/**
* #param {MyProject.MyObject} myObject
* #returns {MyProject.MyObject}
*/
function noOp(myObject) {
return myObject;
}
and
/** #type {MyProject.MyObject} */
const myObject = containerObject.myObject;
With this setup, and the latest public release of VSCode, I was able to see errors in ES6 *.js files that I was currently editing that told me which attributes didn't exist, which were being assigned values of the wrong type, which were assumed to be the wrong type, etc.
Halfway there.
After some more research, I figured out that this isn't exactly a unique VSCode feature. It seems that they're using "tslint" or some customized version of it. Working with that knowledge, I added "tslint" to the project and worked out this npm script:
"tslint": "tslint --type-check --project tsconfig.json"
Here are the contents of the tsconfig.json that I landed on, though I'm not entirely sure all of these options are needed.
{
"compilerOptions": {
"target": "es6",
"allowJs": true,
"noResolve": true,
"checkJs": true,
"moduleResolution": "node",
"types": [
"node"
]
},
"exclude": [
"node_modules",
"coverage",
"lib",
"spec"
]
}
Running this "tslint" script, with that tsconfig.json, and the type definition files in the "typings" folder allows me to type check one specific object type throughout all files in the project with proper JSDoc. I did run into a small issue but that seems to have been fixed and merged about an hour ago, coincidentally. In addition to type checking the object's fields, this also revealed a couple of places where an attribute was prematurely being pulled from an object whose descendant actually had that attribute. Very cool.
TL;DR: It can be done, huge thanks to Sequoia McDowell for that article which finally set me on the right track.
I've found out that you can just set // #ts-check at the beginning of JS files and *.d.ts file will be recognized (at least at the root of the project).
VS Code version: 1.22
Edit:
So I've created jsconfig.json with this content:
{
"compilerOptions": {
"jsx": "react",
"checkJs": true,
"noResolve": true, // because some imports didn't work
"moduleResolution": "node" // because of npm i <git or local dependency>
},
"include": ["src/**/*", "types/**/*"],
"exclude": ["node_modules"]
}
And inside of the types folder I have multiple files *.d.ts.
Everything works fine and you don't need to explicitly set // #ts-check.
You can't strong type in Javascript, and even if you can, you shouldn't, javascript was created to be an dynamic language, but in typescript you can
Checkout this link: https://basarat.gitbooks.io/typescript/docs/quick/nodejs.html
What is the up-to-the-minute advice on getting javascript / jQuery Intellisense in VS Code (1.4.0)? Most answers I have found use TSD, which I understand is (albeit very recently) deprecated.
I have a jsconfig.json
{
"compilerOptions": {
"target": "ES6",
"module": "commonjs",
"allowSyntheticDefaultImports": true
},
"exclude": [
"node_modules"
]
}
I (in this project) have a folder structure open that contains many "websites". I have tried putting the file both at the very root level of the folder structure and at the root level of what I am currently using (i.e. that which i'll run http-server from to fire up the site). No joy.
I have had Intellisense sort of working in other projects, i.e. I can "Go to Definition", and get a method parameters popup, but I have never successfully got the "type . and see the method list" thing which is what I would dearly love!!
After a search for answers yesterday, I tried npm install typings --global, but still nothing at all.
(I am also running eslint - in the unlikely event that that makes any odds (interference?) )
What have I missed?
Typings for many popular libraries are available here, including jQuery.
VS Code can use these with both JavaScript and TypeScript.
As for your JS code, the editor cannot infer all type information due to the nature of JS, so intellisense is limited. If type safety and intellisense support is important for your project, you may consider using TypeScript.
More details on JavaScript support in VS Code.
I have installed 'interact.js' with jspm (and npm for typescript to be happy). The app runs fine but my code shows errors:
import { interact } from 'interact.js/interact'
// ==> typescript error: TS2307: Cannot find module 'interact.js/interact'
I suppose the problem has something to do with the npm module containing '.js' but I am not sure. Anyway, is there a way to fix this either by
A. Help Typescript find the module
B. Disable this specific error (since it works fine)
PS: here is my tsconfig.json file:
{ "exclude":
[ "node_modules"
, "jspm_packages"
, ".git"
, "typings/browser"
, "typings/browser.d.ts"
]
, "compilerOptions":
{ "outDir": "dist"
, "target": "es5"
, "sourceMap": true
, "experimentalDecorators": true
}
, "compileOnSave": false
}
The TypeScript compiler/language service doesn't actually resolve module names through the filesystem or your package.json like you might expect - it instead uses the definition (.d.ts) files that define the type information.
While it's not the most intuitive thing in the world, their reasoning for it wasn't entirely unreasonable - without a definition file, it's impossible to know what type the thing being imported is, and they were somewhat cagey about making the compiler default to just setting imports to the any type.
So in short, the solution to this problem is simply to install the definition files if available, or write/stub out your own if not. They'll be making this easier in TypeScript 2.0 by the sounds of it, but even as it stands, it takes very code to create a dummy definition:
declare module "interact.js/interact" {
export var interact: any;
}