I was learning about typescript but I am getting this duplicate function implementation problem in vs code intellisense whenever I open two different files having same function name but when I close one of the files the error goes away and there is also no errors present when I compile both the files.
Now when I close one of the files the error goes away.
I searched this problem but solutions were for projects and included some tsconfig.json file but here I am just learning the language not doing any projects.
What is happening here? And how can I remove this problem.
Your files do not have static imports or exports, making them ambient files. Similar to how when you link multiple JS files to your HTML without type=module, everything in your files shares one namespace. To fix this, add export {} to each of your files, making them modules with their own namespace.
// ...
export {}; // now a module with own namespace
What I mean by "ambient":
// foo.js
const x = 42;
// bar.js
console.log(x); // x is available in this file
<script src="foo.js"></script> // since you link it here
<script src="bar.js"></script>
Alternatively, I see that you are lacking a tsconfig.json, which is the source of two issues:
the one you are asking about now (conflicting declarations)
compiled js files being in the same folder as your ts files
Making a tsconfig.json will solve both of these. You can get started with that here.
Related
I'm working on a webpage has js files that don't provide esm exports, and they define global variables that other files depend on. Something like this:
<!-- index.html -->
...
<script src="first.js"></script>
<script src="second.js"></script>
...
/* first.js */
var someGlobalVariable = 'value';
/* second.js */
function anImportantFunc() {
doSomethingWith(someGlobalVariable);
}
...
anImportantFunc();
The webpage does not use any UI framework.
I'm trying to minify these js files but the minifier understandably changes the names of the global variables without updating the other files that depend on them, so those other files throw errors when they hit undefined variables. Is there a way to make the minifier aware of this, possibly by specifying variables to ignore in some/all files? Similar to how eslint allows you to specify globals that shouldn't be marked as undefined. I understand this may be difficult because the files don't have clear dependency tree relationships in the way that modules have. I'm using esbuild but open to other build tools if they handle this scenario well. Thanks.
Typescript is awesome, types are awesome, but it's not so easy for some scenarios.
I'm fighting with typescript for a few days, i can't understand where to place my types that need to be used across whole project.
I know that that types can be included in *.ts file, and they will be erased when compiled. But, if i need that type in another module than i need to import it with relative path. But it seems ugly.. if i move that file to another location than i will break half of project.
So i went with idea that types should be independent of the place where it's used.
I've read about declaration merging, and it looks like that is what i want.
My objectives:
I want to build a library (for nodejs) that will be used in another typescript project and (maybe) pure javascript project.
Obviously, the output of my library will be *.js and *.d.ts files. I don't want publish *.ts files because i want compile them once and it may be used in pure js project (i don't want bring typescript compiler there)
One file (module), contains single function.
That function has input and output. Types for input and output is written in d.ts file
declare namespace library.foo {
type main = () => out
type out = {
name: string
}
}
and next to declaration i place file with implementation (foo.ts):
createFoo: library.foo.main = () {
return { name: "baz"}
}
After running tsc i i see this output for foo.d.ts in dist folder:
declare const createFoo: library.foo.main
my hand written declaration file has left in source folder, and it's not in dist folder.
Is there any option in tsc that will emit single declaration file with all my types? That file will be published with generated .js files and all .t.ds files
Update: It seems that, the problem is coming due to protobuf. I am fine with other solution as well, which help me to fix the Google protobuf issues. This problem boils down to:
How to integrate Google protobuf with Typescript/Javascript for the browser?
I am retaining below question for the future purpose.
We have moved our application from Javascript to Typescript for obvious advantages of OOP etc..
Earlier invoking a direct javascript function from Html was as straight forward as:
<script>window.MyFunction()</script>
Now with Typescript, all the files are combined into a single autogenerated .js file.
In this single file, individual code of every file are isolated within System.register(). It typically looks something like:
System.register("<filename>", ["<import_1>", ..., "<import_N>"],
function (exports_13, context_13) {
"use strict";
...
function MyFunction () { ... } // somewhere inside the external function
}
In short, everything written within the .ts file is wrapped in an unnamed function after running the tsc compiler.
Now, I don't know how to invoke a function, which is trapped inside another function, which is in turn listed under System.register(...)
Question: What is the correct syntax to invoke such function externally from an Html file?
<script> ??? </script>
Update:
The HTML tries to invoke in following way in the body tag:
<script>
System.import("Main").then( // Main.ts is one of the file
function (module)
{
throw 0; // Temporary, to see if we reach till here
module.main(); // "main()" is the function, which is the entry point
});
</script>
In my code, I am using "browserify" to be able to use the Google protobuf for JS. The error comes for the protobuf related files only. Those definition and source files are present in .d.ts and .js formats.
The error is something like below:
js: Uncaught (in promise) Error: Fetch error: 404 NOT FOUND
Instantiating http://localhost:50000/folder/external/Server_pb
Loading http://localhost:50000/folder/external/_External
Loading Main
Note that, 50000 is a temporary port and the "folder" is just any folder where the .js are kept. The "Server_pb" is a custom protobuf file generated.
My problem can be aptly described quite similar as this link.
Related:
What is mean by System.register in JS file?
How to call a named module from a bundle (<-- can be helpful, but don't know the syntax as a newbie)
How to start a Typescript app with SystemJS modules? (nearly duplicate, but unable to solve the problem with this approach yet)
How do I get TypeScript to bundle a 3rd party lib from node_modules? (seems like another close match; trying to dig into this right now to fix the protobuf problem)
With "google-protobuf" there are issues when used in the fashion of systemjs. It seems that Google has created it only for the nodejs. :-)
To be able to use the protobuf in Javascript for the browser, there are few things which we have to do manually. Such manual boilerplate work can be done using some scripts as well.
I am giving an iterative way, on how to achieve this:
The first step is to generate the protobuf for both JS and TS. Use following command for the same:
protoc <file1.proto> <file2.proto> ... <fileN.proto>
--proto_path=<proto_folder> \
--cpp_out=<cpp_folder> \
--js_out=import_style=commonjs,binary:<js_folder> \
--ts_out=import_style=commonjs,binary:<ts_folder>
Note that, we are using the commonjs (and not systemjs). Legends:
<proto_folder> = folder path where all these file1/2/N.proto files are stored
<cpp_folder> = folder path where you want the c++ file1/2/N.pb.cc/h files to be stored
<js_folder> = folder where you want the file1/2/N_pb.js files to be stored
<ts_folder> = folder where you want the file1/2/N_pb.d.ts files to be stored
Now in all the .d.ts (Typescript definition) files, there are certain code lines, which will give compiler errors. We need to comment these lines. Doing manually, is very cumbersome. Hence you may use sed (or ssed in Windows, gsed in Mac). For example, the lines starting with,
sed -i "s/^ static extensions/\/\/ static extensions/g" *_pb.d.ts;
same as above for static serializeBinaryToWriter
same as above for static deserializeBinaryFromReader
sed -i "s/google-protobuf/\.\/google-protobuf/g" *_pb.d.ts; // "./google-protobuf" is correct way to import
Now, while generating the *_pb.d.ts, the protoc compiler doesn't follow the packaging for Typescript. For example, if in your fileN.proto, you have mentioned package ABC.XYZ, then the fileN.pb.h will be wrapped in namespace ABC { namespace XYZ { ... } }. The same doesn't happen in the case of Typescript. So we have to manually add these in the file. However, here it won't be a simple find/replace as above. Rather, we have to find only the first occurance of any export class (which is of generated proto) and wrap the namespaces. So below is the command:
sed -i "0,/export class/{s/export class/export namespace ABC { export namespace XYZ {\\n &/}" fileN_pb.d.ts;
sed -i -e "\$a} }" fileN_pb.d.ts;
Initial importing of the google-protobuf package has to be prefixed with ./ in the case of generated _pb.js file as well
sed -i "s/google-protobuf/\.\/google-protobuf/g" *_pb.js;
Now compile the the custom Typescript files with tsc -p "<path to the tsconfig.json>", where the tsconfig.json may look like (see arrow):
{
"compileOnSave": true,
"compilerOptions": {
"removeComments": true,
"preserveConstEnums": true,
"module": "CommonJS", <=======
"outDir": "<path to generated js folder>",
},
"include": ["../*"],
"files": ["<path to file1.ts>", ..., "<path to file2.ts>"
}
Now a very important step. All the references to the the generated *_pb.d.ts files, should be referred in 1 of your custom file. That custom file may contain the wrappers around the generated classes if it's required. This will help in limiting string replacement only in that file, which is explained in the upcoming step. For example, create a custom file name as MyProtobuf.ts and import your proto as following:
import * as proto from './fileN; // from fileN.d.ts
In above step, it's important to note that the name "proto" is crucial. With that name, the .js files are auto generated. If there are several proto files in your project, then you may have to create yet 1 more file which exports all of them and then import that 1 file:
// in 'MyProtobufExports.ts' file
export * from './file1'
export * from './file2'
export * from './fileN'
import * as proto from './MyprotobufExports // in MyProtobuf.ts file
With above 2 steps, the usage of the protobuf as, var myClass = new proto.ABC.XYZ.MyClass;
Now the continuation of the important step we discussed above. When we generate the equivalent _pb.js and our custom .js files, still the special name-symbol proto will not be found somehow. Even though everything is wrapped. This is because the autogenerated JS files (from TS files), will declare a var proto. If we comment that then, that issue is gone.
sed -i "s/var proto = require/\/\/ &/g" Protobuf.js;
The final step is to put the browserify on all the .js files into a single file, as below. Due to this, there will be only single .js file, we have to deal with [good or bad]. In this command, the ordering is very important. file1_pb.js should come before file2_pb.js, if file1.proto is imported by file2.proto or vice a versa. If there is no import then the order doesn't matter. In any case the _pb.js should come before the custom .js files.
browserify --standalone file1_pb.js fileN_pb.js MyProtobuf.js myfile1.js myfileN.js -o=autogen.js
Since the code is browserified, the calling of function can be done in following way:
window.main = function (...) { ... } // entry point somewhere in the fileN.ts file
<script>main(...)</script> // in the index.html
With the above steps only, I am able to make the "google-protobuf" work within my project for the browser.
I have quite an annoying, but probably simple, problem that I just cannot figure out.
In a TypeScript file I have defined the following line:
import test1 = require('domReady');
This "domReady" module is defined in a main.js file that is loaded as the entry point for RequireJS. The definition is as followed:
require.config({
paths: {
'domReady': '../domReady',
}
However... in my TypeScript file I simply get a "cannot find module 'domReady'" and it is driving me insane, as I have double checked the pathing to the file and it is indeed in the correct location with the correct name. Additionally, I am fairly certain that the domReady.js file IS AMD compatible, so it should define an external module just fine! (domReady GitHub Link).
I seriously can't understand why the module can't be found in the import statement. Does anyone have any ideas to what the problem may be?
EDIT 1
The directory structure is as follows:
.
+--App
| +--main.js
| +--dashboard.js
+--domReady.js
The import statement takes place in the "dashboard.js" file, and the config for require.js happens in "main.js".
In order for TypeScript to find a module, you must actually provide module information to TypeScript.
TypeScript doesn’t yet support AMD-style paths configuration, it doesn’t ever use calls within your JavaScript code (like require.config()) to configure itself, and it won’t treat JavaScript files on disk as modules when compiling. So right now, you aren’t doing anything to actually give the compiler the information it needs to successfully process the import statement.
For your code to compile without error, you have to explicitly declare an ambient declaration for the module you’re importing within the compiler, in a separate d.ts file:
// in domReady.d.ts
declare module 'domReady' {
function domReady(callback: () => any): void;
export = domReady;
}
Then, include this d.ts in the list of files you pass to the compiler:
tsc domReady.d.ts App/main.ts App/dashboard.ts
Any other third party JavaScript code that you import also needs ambient declarations to compile successfully; DefinitelyTyped provides d.ts files for many of these.
I've had problems before when the path key and the directory name or file name are the same (in your case, domReady). Might not work, but worth giving it a quick try, i.e.
either
'domReadyModule': '../domReady',
require('domReadyModule')
or rename domReady.js to e.g. domReady-1.0.js and use
'domReady': '../domReady-1.0',
require('domReady')
If that doesn't work, I'd check the relative directories between main.js and the file that is doing the require, or else try importing another library in the same fashion, or finally compare with other libraries that you are importing successfully.
Good luck, hope you resolve the problem!
I'm trying to migrate large node codebase to TypeScript. To make things easier, I just want to start by renaming .js files to .ts files, fix any semantic issues, without much refactoring, and then gradually improve the code base.
Let's consider the following example:
logger.js:
exports = function(string) {
console.log(string);
}
moduleA.js:
var logger = require('./logger')
exports.someAction = function() {
logger.log('i'm in module a')
}
moduleB.js:
//var logger = require('./logger')
exports.someOtherAction = function() {
logger.log('i'm in module B')
}
moduleC.js:
var moduleA = require('./moduleA');
var moduleB = require('./moduleB');
exports.run = function(string) {
moduleA.someAction();
moduleB.someOtherAction(); //exception here - logger not defined
}
So if i execute moduleB.someOtherAction() i'll get an exception, because logger is not defined in scope in moduleB.js.
But typescript compiles just fine, because logger is declared in moduleA, and this is because (if i understand correctly) typescript treats all of those files as a single compilation unit.
So is there anyway to avoid this without much refactoring?
Update
I've created a sample project which can be found here
If i run typescript compiler, i get no errors, though logger is commented out in moduleB.ts:
g#w (master) ~/projects/ts-demo: gulp generate
[10:39:46] Using gulpfile ~/projects/ts-demo/gulpfile.js
[10:39:46] Starting 'generate'...
[10:39:46] Starting 'clean'...
[10:39:46] Finished 'clean' after 11 ms
[10:39:46] Starting '<anonymous>'...
[10:39:47] Finished '<anonymous>' after 1.4 s
[10:39:47] Finished 'generate' after 1.41 s
g#w (master) ~/projects/ts-demo:
Update 2
Ok, so this is expected behaviour as stated in TypeScript deep dive book:
If you now create a new file bar.ts in the same project, you will be allowed by the TypeScript type system to use the variable foo from foo.ts as if it was available globally
Louy is right. Every file (when compiling for node with CommonJS) is created like its own module. Just like the normal case in node.
So to get this to work you could do something like this:
logger.ts
export default (str: string) => console.log(str);
moduleA.ts
import logger from './logger';
export var someAction = () => {
logger("i'm in module a");
}
moduleB.ts
import logger from './logger';
export var someOtherAction = () => {
logger("i'm in module B");
}
moduleC.ts
import { someAction } from './moduleA';
import { someOtherAction } from './moduleB';
someAction();
someOtherAction();
I also used a tsconfig.json:
{
"compilerOptions": {
"module": "commonjs",
"noImplicitAny": true
},
"files": [
"logger.ts",
"moduleA.ts",
"moduleB.ts",
"moduleC.ts"
]
}
Compiling this (just type tsc in the folder containing the tsconfig.json) results in one .js file per .ts file. It should compile and run just fine. The main thing you need to take from this is that each file is its own self contained module when compiling for node/CommonJS.
EDIT Due to edits in question:
Ok after looking at your code again there is a couple of reasons that this compiles but fail to run properly:
If you send a group of files to the compiler all references in all of those files, and all of the variables in the global scope will be available in all .ts files sent to the compiler. This is confusing and sometimes regrettable, but at the moment it is how things work. This in your case results in that the require function defined in the global scope in node.d.ts is available in all .ts files, not only in moduleC.ts; if the reference is removed from moduleC.ts you will get an error in logger.ts because the require function is not defined for example. It also results in var logger = .. defined in logger.ts will be seen as available in the other .ts files. Typescript assumes that it will be available at run time.. not ideal when compiling for node, but not a problem when you are writing actual typescript not trying to compile pure javascript. In fact, because it is pure javascript the compiler does not recognise it as a node module, and treats it as a generic javascript file.
When compiling with commonjs each file is compiled on its own by the typescript compiler. If any references or imports are found those will be followed and used to type and verify the code of course, but the compilation is still done on a per file basis (var something = require('./whatever'); is not an import, it is interpreted by the compiler as a variable being assigned by a function taking a string as an argument.)
So thats why it compiles, lets go on to why its not working then.
Each file outputted by the compiler in commonjs mode is its own "node-module". If you want to have access to one module from within another in node (this has nothing to do with typescript really, it is just how node works) you will need to require that file. in moduleB.ts (and in moduleB.js for that matter) there is no indication to node to say what logger is. So you simply need to require logger.
In the example you are referring to in your second update of the question commonjs is not being used, and further more it is in connection with typescripts internal modules - not node modules which is a completely different thing.
So the simple answer is, that if you want to use one file from within a another in node you have to require it. Simply uncommenting the commented reference in moduleB.ts should get everything up and running.
In the current state of your code the typescript compiler is not helping you much, because it can't really. The code is close to pure javascript, to get decent help from the compiler you will need to start converting it to typescript. The first step might be to replace var asdf = require("") with import asdf = require("");, this will make it possible for the compiler to check the references and help you a lot in return.
I realise this is a wordy answer, but you managed to create yourself a confusing and difficult to explain behaviour.
There's something called internal/external modules in typescript.
Internal modules can declare stuff that can be referenced everywhere in the project. Think about typings/lib files, you should be able to reference things like Error and String everywhere in your project.
External modules on the other hand have to be imported to be referenced. This is what you want to do.
The difference between internal and external modules is that external ones use import and export.
Internal ones use declare module/namespace/var.
Just add "compilerOptions": {"module": "commonjs"} to your tsconfig.json file to enable external modules.