What is a good practice for adding flow type annotations on project specific named exports - flowtype

Please correct me if i am wrong. As far as i understand it up till now; type annotations can be added to a file or in libdefs (for shareable code)
For example in a project specific file helpers.js
// #flow
export function square(value: number): number {
return value * value
}
export function someOtherFunction(arg: string): string {
}
etc...
And in a libdef helpers.js
declare module 'helpers' {
declare export function square(value: number): number;
declare export function someOtherFunction(arg: string): string;
}
What would be a good practice for writing flow annotations on project specific code and especially lots of code. For example helpers exposing 20+ named exports, as this is the point where i am starting to think having a libdef would be more clearer to reason about.
And is it at all possible to use that libdef file as the single entry? I've fooled around a bit and i always had to annotate in the file itself even though i had added the libdef and told flow through the config to include these libdefs.

In our project, we use the following approach:
// #flow
export const square: SquareType = (value) => {
return value * value;
}
So you can declare SquareType in the helpers.js file just above the function or you can move it to a separate file and import it then into helpers.js

Many third-party modules don’t have types or only TypeScript types.
And libdefs need for one reason. To declare types for untyped modules!
More info: https://flow.org/en/docs/libdefs/

Related

Is it a good idea to nest constants in Redux?

In our Product we use Angular 6 together with NgRX 6. Instead of defining our constants as export const strings, we use an object to encapsulate them:
export const ACTION_CONSTANTS = {
'OPEN_MODAL' : 'OPEN_MODAL',
'CLOSE_MODAL' : 'CLOSE_MODAL',
'OPEN_TOOLTIP' : 'OPEN_TOOLTIP',
'CLOSE_TOOLTIP' : 'CLOSE_TOOLTIP',
...
};
As the ACTION_CONSTANTS object gets bigger and prefixes get longer ('DROPDOWN_ACTION_SKIP_SET_INIT_STATE'), I would prefer to nest constants e.g. by feature:
export const ACTION_CONSTANTS = {
'MODAL' : {
'OPEN' : 'MODAL.OPEN',
'CLOSE' : 'MODAL.CLOSE'
},
'TOOLTIP' : {
'OPEN' : 'TOOLTIP.OPEN',
'CLOSE' : 'TOOLTIP.CLOSE'
},
...
};
Is it a good idea or are there any downsides? I could not find anything on formatting constants on the Redux FAQ.
I don't think it is a bad idea, as long as you're able to keep it all organized. But I would suggest grouping your actions into different files. I find this the best way to keep things organized.
--ActionsFile
-modalActions.js
-toolTipAction.js
I usually keep actions in different files, roughly aligned with models & reducers. And i have a naming convention like:
ACTION_MODEL_OUTCOME
So, for example, to load model of type ProductGroup i would have actions:
export const ActionTypes = {
LOAD_PRODUCTGROUP: enforceUnique("[ProductGroup] Laod ProductGroup"),
LOAD_PRODUCTGROUP_SUCCESS: enforceUnique("[ProductGroup] Load ProductGroup Success")
LOAD_PRODUCTGROUP_FAILURE: enforceUnique("[ProductGroup] Load ProductGroup Failure")
}
enforceUnique is a function that caches all registered actions and make sure there are no duplicates across the whole app.
Now, when you import actions for certain model, you import only those from file you need (e.g. import ProductGroupActionTypes from 'actions/ProductGroupActions') and use them like ProductGroupActionTypes.LOAD_PRODUCTGROUP.
Usually, first one (without outcome suffix) is the one to initiate action and set some pending flag in reducer to show loader and also to initiate http calls in #Effects.
Second one, with success suffix is handled in reducer to change state.
Third one is error handling, whatever way you want to do it.

How to implement redux-search

I am trying to implement a search filter in my application which uses react/redux using redux-search. The first gotcha I get is when I try to add the store enhancer as in the example.
// Compose :reduxSearch with other store enhancers
const enhancer = compose(
applyMiddleware(...yourMiddleware),
reduxSearch({
// Configure redux-search by telling it which resources to index for searching
resourceIndexes: {
// In this example Books will be searchable by :title and :author
books: ['author', 'title']
},
// This selector is responsible for returning each collection of searchable resources
resourceSelector: (resourceName, state) => {
// In our example, all resources are stored in the state under a :resources Map
// For example "books" are stored under state.resources.books
return state.resources.get(resourceName)
}
})
)
I understand evarything up to the resourceSelector, when I tried to get a deep dive into the example to see how it works but I can barely see how they are generated and the last line returns an error, Cannot read property 'get' of undefined
My state object looks like this
state: {
//books is an array of objects...each object represents a book
books:[
//a book has these properties
{name, id, author, datePublished}
]
}
Any help from anyone who understands redux-search is helpful
If this line:
return state.resources.get(resourceName)
Is causing this error:
Cannot read property 'get' of undefined
That indicates that state.resources is not defined. And sure enough, your state doesn't define a resources attribute.
The examples were written with the idea in mind of using redux-search to index many types of resources, eg:
state: {
resources: {
books: [...],
authors: [...],
// etc
}
}
The solution to the issue you've reported would be to either:
A: Add an intermediary resources object (if you think you might want to index other things in the future and you like that organization).
B: Replace state.resources.get(resourceName) with state[resourceName] or similar.

How do I extend an array with the Flow type checker?

In a React project I'm playing around with the MobX library. One thing it allows you to do is to write your code like you are working with the primitives but under the hood MobX is doing some observable magic. I'm also using Flow in this project and am having a hard time figuring out the right syntax to apply to the MobX observable arrays which have at least two extra methods, replace and peek.
The TS file for this code is pretty in depth and I'm fairly new to typing. The TS file can be found here:
https://github.com/mobxjs/mobx/blob/master/src/types/observablearray.ts
None of the syntax that I've tried in Flow will work, it is probably all pretty naive/ignorant.
interface ObservableArray {
join(str: string): string;
replace(arr: Array<any>): ObservableArray;
map(mapFunction: Function): Array<any>;
reduce(reducerFunction: Function, initialValue: any): any;
peek(): Array<any>;
}
type MobxArray = ObservableArray;
or
interface ObservableArray extends Array {
peek(): Array<any>;
replace(arr: Array<any>): MobxArray<any>;
}
type MobxArray = ObservableArray;
Most of the time I get this error "Expected polymorphic type instead of type 'MobxArray'." I'm getting this error when I'm trying to declare the props for a component:
type MyComponentProps = {
myArray: MobxArray<string>;
};
const MyComponent = observer(({ myArray }: MyComponentProps) => (
<div>{ ... code ... }</div>
));
I probably had this solution long ago but my ESLint config was complaining and I probably never flow checked the results of this because of the no-undef error.
interface MobxArray<V> extends Array<V> {
replace(arr: Array<V>): MobxArray<V>;
peek(): Array<V>;
}
I don't know whether automatic conversion of TypeScript .d.ts files to flow is possible, but if you look into the package itself, you will find all interfaces in .d.ts files in the lib/ directory, that probably makes the conversion easier (those are not on github as they are generated during build)
Edit: maybe you can even refer directly to mobx/lib/index.d.ts, the flow syntax for interfaces seems pretty similar.

Where to put helper methods used in events for multiple Meteor templates

I know that I can access methods across files by omitting var, but what is the "best practices" project directory structure for defining helper methods that are used by template events across different templates.
For example, I have:
template1.js:
Template.template1.events({
'event': function () {
helper();
}
});
template2.js:
Template.template2.events({
'event': function () {
helper();
}
});
One problem with Meteor's "share code across files with globals" approach is that when you're looking at the file where a function is used, you don't know where the function is defined. I prefer to define a single global variable in each file which needs to export variables. That variable has the same name as the file, with an initial capital (or some other naming convention which identifies it as an "export object"). Exports are stored as properties of that object. So you might create a file named globalHelpers.js:
GlobalHelpers = {};
GlobalHelpers.helper = function () {
// ...
};
And then use it in other files with GlobalHelpers.helper(). If you look at this code later, you know to look in globalHelpers.js to find the helper function.
If a file exports a single class or collection then it's okay to just use that object as the export object. So instead of things.js with Things = {}; Things.Things = new Mongo.Collection... you can just do Things = new Mongo.Collection....
You might need to place the file in a lib directory so it loads before the other files.
Don't register it with Template.registerHelper unless you want to call it directly from templates with {{ }} calls.
I suggest defining such functions in /client/scripts/globalHelpers.js
Example:
Template.registerHelper('foo',function(arg1,arg2){
return arg1 + arg2;
});
Then you can use this function from anywhere by prefixing it with Blaze._globalHelpers.:
var result = Blaze._globalHelpers.foo(param1, param2);

TypeScript Defining a hash table of functions

I'm trying to create a definition file for Handlebars, for use with pre-compiled handlebar scripts. Handlebars will put pre-compiled scripts into a string indexed hash table of functions, but I can't figure out or find how this would be defined.
A hypothetical definition would be:
declare module Handlebars {
export var templates: { (model:any) => string; }[index: string];
}
but that's not a valid definition. The definition should work for a call like this:
var myHtml = Handlebars.templates["person-template"]({FNmae: "Eric"});
A definition like this is close:
export var templates: { (model:any) => string; }[];
But that's an array with a numeric index, and it's not the same thing, and VS Intellisense just decides that the functions in the array are any.
What you want to use is an object type with an index signature (see spec section 3.5.3, specifically 3.5.3.3).
declare module Handlebars {
export var templates: {
[s: string]: (model: any) => string;
}
}

Resources