how to remove Individual declarations in merged declaration must be all exported or all local.ts error? - node.js

I am implementing a redux store and I am pretty sure this is not a redux issue, in my store file, dog.store.ts I am exporting the store initial state:
export interface IDogState{
dog:number;
}
export const INITIAL_DOG_STATE:IDogState = {
dog:0
}
to later import it in my store.ts file:
import { IDogState,dogReducer,INITIAL_DOG_STATE } from "./redux/stores/dog.store";
this is where I get the error:
Individual declarations in merged declaration 'INITIAL_DOG_STATE' must
be all exported or all local.ts(2395)
Binding element 'INITIAL_DOG_STATE' implicitly has an 'any' type.
this was not an issue not long ago, I wonder if it is because a change in typescript or Angular compiler configuration.

Related

Typescript - Dynamic class Type

I'm trying to build a sort of model Factory in Typescript.
I'm receiving a string parameter from an API call and I would like to istantiate a new object depending on the received value.
Here you can find a simple example of what I would like to accomplish:
/classes/ClassA.ts
export class ClassA {
doSomething() {
console.log("ClassA");
}
}
/classes/ClassB.ts
export class ClassB {
doSomething() {
console.log("ClassB");
}
}
/classes/index.ts
import { ClassA } from './ClassA';
import { ClassB } from './ClassB';
export { ClassA, ClassB }
Now, I would like to import all classes exported from index.ts (this file will be automatically updated when a new Class is being created) and run doSomething() on a class depending on a variable value:
/index.ts
import * as Classes from './classes';
const className: string = "ClassA";
new Classes[className]().doSomething()
In visualStudioCode I don't get any error, but at compile time I get:
error TS7053: Element implicitly has an 'any' type because expression of type 'any' can't be used to index type 'typeof import("/testApp/src/tmp/classes/index")'.
Even changing className to "any" gives the same result.
If I remove className type
const className = "ClassA";
it works without any issue but I cannot proceed in this direction because received value is "typed" as string.
I know that prepending istantiation code with
// #ts-ignore
It works but I would like to avoid this kind of "tricks"
So, what would it be the correct way to type className getting it's possible values from the imported ts?
Thanks
Micko

declare global variable in seperate file nodejs+typescript

I am new to typescript and this might be a noob question.
I want to extend global variable provided by nodejs.
As per this blog I wrote this code and it is working
declare global {
namespace NodeJS {
interface Global {
appRoot: string;
}
}
}
import path from "path";
global.appRoot = path.join(__dirname,'../');
console.log(global.appRoot)
but I want to take this global to separate file and if I move this to a new global.d.ts file
I dont know what to export
I am getting this error
Augmentations for the global scope can only be directly nested in
external modules or ambient module declarations.
if do this
declare module NodeJS {
export interface Global {
appRoot: string;
}
}
I get this error
Property 'appRoot' does not exist on type 'Global & typeof globalThis'.
Property 'appRoot' does not exist on type 'Global & typeof globalThis'.
Which version of global declaration works always seems to depend on project setup. In your case, the following global.d.ts should work:
export {}; // make the file a module, to get rid of the warning
declare global {
namespace NodeJS {
interface Global {
appRoot: string;
}
}
}
Also make sure that only either one of the definitions is present.

Static data initialised twice due to typo in import in TypeScript

I have a newbie question on TypeScript imports. I tried to make a class which holds some data in a static variable, and the data is lazily initialised in getInstance() method.
myStaticClass.ts:
class MyData {
x = 1;
}
export class MyStaticClass {
private static data: MyData;
static getInstance() {
if (MyStaticClass.data == null) {
console.log('data is null, initialising');
MyStaticClass.data = new MyData();
}
return MyStaticClass.data;
}
}
I imported this class in 2 other classes:
a.ts
import { MyStaticClass } from './MyStaticClass';
// NOTE the typo above - uppercase file name
export class A {
logX() {
console.log(MyStaticClass.getInstance().x);
}
}
index.ts
import { MyStaticClass } from './myStaticClass';
import { A } from './a';
console.log(MyStaticClass.getInstance().x);
new A().logX();
To my surprise, the output of ts-node index.ts is
data is null, initialising
1
data is null, initialising
1
If I correct the import the output is as expected - data is initialised only once.
I also checked that I get one initialisation for one variant of spelling (added 3rd class with another letter in upperCase)
Can anyone explain why this behaviour is in place?
(Additionally, what tools / debug statements I could have used to identify what is happening?)
Can I force TypeScript to flag this as error?
I am on MacOs, TS 3.6.3, node-ts 8.4.1
While on Windows two differently cased file names always point to the same file, on other platforms they do not. This means that imports with different file names are treated as different modules, this is by design and is not considered an issue (see here for node discussion)
The simple solution is to force consistent file casing to be used when importing modules. Typescript does have a compiler option to force this named forceConsistentCasingInFileNames (See docs). This option should prevent such issues.

Type hints of flow not stripped by babel

I have a React.JS project which uses a custom 'theme' with UI components.
This theme also provides build scripts (webpack config, babel configs, etc.).
I want to start using Flow in this project.
I installed the needed npm packages and added flow to babel's presets, then I added props = {mytestprop: string} to one of my React` classes.
Webpack compiled my code successfully, but the type hints were not stripped! Of course, the browser was not able to execute this code - when I try to run it, it raisesReferenceError: string is not defined.
The current list of presets from .babelrc is: ["es2015", "react", "stage-2", "flow"]. I'm sure that this is the actual list used by babel because if I delete any of the first 3 presets, compilation fails.
Do you have any ideas on what could lead to this behavior when stripping Flow types?
It's not that type annotations are not being stripped. It's that { mytestprop: string } is not a valid type annotation on the right-hand side of an assignment because it clashes with the syntax for defining an object.
Specifically, when Flow's parser sees the statement { mytestprop: string } it will interpret this as an attempt to create an object with a field named mytestprop with its value set to the value of the variable string, so it will leave the statement alone as it is, and you'll get the error you've seen in the browser.
The correct way to type object declarations is to type the left-hand side of the declaration.
For instance,
let myProps: { myTestProp: string } = { myTestProp: "testProp" };
if you aren't declaring your props separately, you could declare a custom type:
type myPropType = { myTestProp: string }
// ...
const myComponent = (props: myPropType) => //render your component
Since the type statement is exclusive to Flow and not a valid JavaScript statement, it will be stripped correctly.

TypeScript Cannot find namespace despite Variable being in same Class

I'm trying to define Callbackdefinitions to make it easier to work with many callbacks in my Node.js project.
My database.ts file looks like that:
export default class Database {
//Export the enums
public static LoadObjectResponse = LoadObjectResponse;
//Export Callback definitions
public static loadObjectCallback: (resultCode: Database.LoadObjectResponse) => void;//ERROR
...
}
enum LoadObjectResponse {
ERROR_ON_LOADING, //"Error on Loading Object.",
OBJECT_NOT_FOUND //"Object not found."
}
So I want a loadObjectCallback defined, that says that the parameter has to be of the enum Type LoadObjectResponse. But when I try to do it like that, the compiler always gives the error
Cannot find namespace "Database"
I don't understand why it gives me the error, the variable itself is in the definition of Database, why doesn't it work?
It gives me the same error when I try to use it in Classfunction definitions:
public static loadObject(MongoModel, searchObject, callback: Database.LoadObjectResponse) {//ERROR namespace Database not found
Again Error:
Cannot find namespace "Database"
Inside of functions in the Database class calling
Database.LoadObjectResponse
works flawlessly, why doesn't it work in variable definitions?
Cannot find namespace "Database"
This is a common learning curve issue. You need to understand and be comfortable with the intutive concept of declaration spaces : https://basarat.gitbook.io/typescript/project/declarationspaces
Things are distinct in the type declaration space or in the variable declaration space.
In your case public static LoadObjectResponse is a variable hence cannot be used as a type (error on annotation usage : Database.LoadObjectResponse).
Fix
Please don't treat a class as a namespace. The file is a module.
export class Database {
//Export Callback definitions
public static loadObjectCallback: (resultCode: LoadObjectResponse) => void;//ERROR
}
export enum LoadObjectResponse {
ERROR_ON_LOADING, //"Error on Loading Object.",
OBJECT_NOT_FOUND //"Object not found."
}
Also beware of export default : https://basarat.gitbook.io/typescript/main-1/defaultisbad
It's because Database.LoadObjectResponse is a property and not a type. You can't use properties as types.
To make this work, change it to use the type of the property:
static loadObjectCallback: (resultCode: typeof Database.LoadObjectResponse) => void;
Or refer directly to the enum type of LoadObjectResponse:
static loadObjectCallback: (resultCode: LoadObjectResponse) => void

Resources