Import classes into the global scope - javascript

I am trying to import a class into the global scope, and I am able to do it, but then when I try to extend the class I get an error saying:
Type 'any' is not a constructor function type.
So this is what I am doing to get that error:
main.ts
const MyClass = require('./core/MyClass');
class MyTestClass extends MyClass {
}
I then tried using import MyClass from './core/MyClass', instead of a const, but that seems as if it is only within the scope of the current file, which doesn't help me either.
core/MyClass.ts
export default class MyClass {
}
I have tried using namespaces which works the way I want in typescript, but once compiled into javascript the namespace is undefined.
Is there a way I can import my class into the global scope and not get the error above when extending the class?

In your code you have :
require('./core/MyClass');
If you don't have import / export in your file then TypeScript assumes the file is global. However depending upon your usage of the file (e.g in NodeJS or if using a bundler like webpack) the file is still a module and not global.
Cool, with that out of the way you can put something on the global like:
export default class MyClass {
}
(global as any).MyClass = MyClass;
Be sure to include node.d.ts to get global.
More
And of course I would also like to warn against default as the const / require you wrote is also wrong. You need something like const {default} = require('module/foo');. More: https://basarat.gitbooks.io/typescript/content/docs/tips/defaultIsBad.html 🌹

Related

Typescript call a javascript function in current scope

I'm migrating a project from JS to Typescript. But new TS class should coexist with previous JS. There is a function in scope called verifyActivityChange(). In javascript this function has been declared in the app.js file and can be called from anywhere.
How do I call this function from a Typescript class ?
In some TS file I was able to do
// #ts-ignore
import * as appjs from "./app.js";
appjs.verifyActivityChange();
Note sure if this is correct since I had to ignore compilation error. But if I'm doing the same process within a class I have an error appjs dosn't have a constructor.
Edit How do I call the function from the class
import {verifyActivityChange} from "./app.js";
export class ViewController {
public update(){
verifyActivityChange(); //how can I call this function ? it's on my page DOM
}
}
You can import JS files in TS code, but you have to do inform the compiler that you are trying to do this. You can either compile with the --allowJs option (read more here) or specify this in the tsconfig.json file, read more here)

Declare a typescript module that is both a type and a value

I'm making typings for the WordPress wp.customize API. However, I've hit a snag and don't know, how to declare the top-level export.
The problem is following:
wp.customize extends a class called Values, that is defined in the "module". On top of that, this class has a call signature (because it really isn't a typescript class but a myriad of horrible JS hacks). So you need to be able to call wp.customize()
wp.customize has member variables and functions. So these need to be accessible, e.g. wp.customize.get()
wp.customize also contains classes & types such as Setting, so you nned to be able to do
function xxx (a: wp.customize.Setting){
I don't know how to do these all at the same time. I am not the author of the library, so I can in no way change it, I just want to create typings for it. What I did is:
// index.d.ts
import {Customize} from './Customize';
declare const customize: Customize;
export = customize;
// Customize.d.ts
export interface Customize extends Values {
Setting: typeof Setting;
get(): any;
}
I would ideally like to import the typings in the same way other WordPress typings are implemented, but this is not strictly necessary:
declare const wp: {
customize: typeof import("wordpress__customize");
};
However, then I can't use the types, such as wp.customize.Setting. I get error TS2503: Cannot find namespace 'wp'.

How can I describe a JavaScript class static factory method in TypeScript typings with default export?

I'm developing a vanilla JavaScript library and have a JS class defined like this:
class MyClass {
static create() {
return new MyClass();
}
doOneThing() {
// ...
return this;
}
doOtherThing() {
// ...
return this;
}
}
module.exports = MyClass;
So I can use dsl-like syntax:
MyClass
.create()
.doOneThing()
.doOtherThing();
And I'm trying to add d.ts typings file for this class for fellow TypeScript developers could use it too.
Best I've been able to get so far:
export interface MyClassInstance {
doOneThing(): MyClassInstance;
doOtherThing(): MyClassInstance;
}
export interface MyClassStatic {
create(): MyClassInstance;
}
declare const MyClass: MyClassStatic;
export default MyClass;
So it works for TypeScript:
import MyClass from "./src/MyClass";
But in a JavaScript file my IDE offers me this via autocomplete tool:
MyClass.default.create();
I've figured I could add mandatory destructuring, exporting my class wrapped in an object:
module.exports = {MyClass};
Then both TS and JS work the same way. But I rather won't.
So I wonder, if there is another way to do that - to have both static method and default export worlkng both in JS and TS
UPDATE 1
It appears I can declare a class instead of an interface in my .d.ts file like this:
declare class MyClass {
static create(): MyClass;
doOneThing(): this;
doOtherThing(): this;
}
export default MyClass;
Both JS and TS seems to work fine with it, but I'm still checking out if it's OK or kinda "don't" or bad practice.
UPDATE 2
Well, it seems it's the best I can get.
I also checked DefinitelyTyped repo and some modules use class declaration in their typings, so I guess it's OK.
Check the section export = and import = require():
// change to
export = MyClass;
If is not for the exercise in itself, you could write in TS and pass --declaration to the compiler and let it generate the .d.ts file

How do we declare import types from NPM library that has no declaration files?

For example, if I have the following in my app,
import Node from 'infamous/motor/Node'
console.log(Node)
that works just fine. But the moment I actually do something with it,
import Node from 'infamous/motor/Node'
console.log(new Node)
then TypeScript will complain because there's no type definition for Node. How do I define the type of Node?
The library has no type declarations of it's own. I tried something like
import MotorNode from 'infamous/motor/Node'
declare class MotorNode {}
console.log(' --- ', new MotorNode)
but I get the error
./src/app.tsx(6,8): error TS2440: Import declaration conflicts with local declaration of 'MotorNode'
When I need to do what you are trying to do, I create an externals.d.ts file in which I put module augmentations for my project and make sure that my tsconfig.json includes it in the compilation.
In your case the augmentation might look something like this:
declare module "infamous/motor/Node" {
class Node {
// Whatever you need here...
}
export default Node;
}
I put it in a separate file because a module augmentation like this has to be global (must be outside any module), and a file that contains a a top-level import or export is a module. (See this comment from a TypeScript contributor.)

In VSCode when exporting functions: "Individual declarations must be all exported or all local"

I recently upgraded to Visual Studio Code 0.5.0 and some new errors cropped up that weren't there before.
I have a bunch of functions that are declared locally and then exported. Since the upgrade, however, hovering over each of the local function names produces the error Individual declarations in merged declaration functionName must be all exported or all local.
This is an example local function that is exported.
var testParamsCreatorUpdater = function (lTestParams, creatorID){
lTestParams.creator = creatorID;
return lTestParams;
};
module.exports.testParamsCreatorUpdater = testParamsCreatorUpdater;
I realize I can change this to...
module.exports.testParamsCreatorUpdater = function (lTestParams, creatorID){
lTestParams.creator = creatorID;
return lTestParams;
};
And prepend module.exports. to every testParamsCreatorUpdater() call.
But why is the first snippet wrong? As I understand it, require() makes everything in the module.exports object available to whatever required it.
I had this issue in Webstorm , I Restarted it and it went away
I think at a JavaScript level it cannot differentiate between:
var testParamsCreatorUpdater = ...
and
module.exports.testParamsCreatorUpdater = ...
as the names are the same. I got the exact same error (leading me to this post) in TypeScript when I tried this:
import { AuditService } from '../services/audit.service';
import { Audit } from '../models/audit.model';
#Component({
selector: 'audit',
templateUrl: './audit.component.html',
})
export class Audit {
constructor(private auditService: AuditService) {
}
}
So TypeScript did not like that I imported a module called Audit and exported a class also called Audit.
You are exporting a variable in this file which is imported in the same file module (locally).
I think it's related to the feature of merged declaration for TypeScript ref. I have not done the detailed research for Typescript but it seems that it can include Javascript in the Typescript file.
I guess the way testParamsCreatorUpdater was declared in the Javascript was detected to be error by VSCode because it thinks the two declarations cannot be merged.
So DuckDuckGo got me here searching for the exact same error, but in 2022. I didn't find the exact reason so posting as an update and for completeness.
import { Something, SomethingElse } from './some/path';
import { ref } from 'vue';
// Many lines of code
function doTheStuff() {
// The declarations were previously just local variables ...
// const Something = ref<Something>();
// const SomethingElse = ref<SomethingElse>();
}
// ... but then I decided to export them and got the error
export const Something = ref<Something>();
export const SomethingElse = ref<SomethingElse>();
You simply can not import Something as a type and then export Something variable as a value of a kind (here a vue reference object). However, you can name a local variable the same name as the type, like I originally had. It is the import/export combination where things got broken. Solution for me was to locally rename the types:
import {
Something as SomethingType,
SomethingElse as SomethingElseType
} from './some/path';
import { ref } from 'vue';
// ...
// No naming conflict anymore
export const Something = ref<SomethingType>();
export const SomethingElse = ref<SomethingElseType>();

Categories

Resources