Encaps string in flow - flowtype

I created this flow demo - https://flow.org/try/#0PQKgBAAgZgNg9gdzCYAoVBjOA7AzgFzAEEwBeMACgEMN8BLHAFQE8AHAUwC4wCAnO7AHMAlGQB8YAOQBhAPIBVAHKMAogCUA+pLABqMDXpM27MMGDFaDbCw4AFXuyh0AHu17oseQgCEYVABZkxAAGAEZ+-sEA3EA
I am using React Native and Flow.
My code is:
const A = (actionType: string) => 'COUNTER_' + actionType // ActionTypePrefixer
const UP = A`UP`;
My goal is to call a function with backticks.Flow highlights this as an error saying:
Is there any way to do encaps with:
[flow] array (This type is incompatible with the expected param
type of string See also: encaps tag)
Screenshot:

Flow highlights this as an error
That's because it is an error! The first parameter to a template literal function is an Array<string>, not a string. Your code would still execute because ['foo'].toString() === 'foo', but it would easily break in the general case. Therefor you should change
const A = (actionType: string) => 'COUNTER_' + actionType;
to
const A = (actionType: Array<string>) => 'COUNTER_' + actionType[0];
Potentially it might also be good to throw an exception if actionType.length > 1.

Related

Searching a DynamoDB String Set Attribute by Array of Strings

I am struggling to find the correct syntax to search a String Set/List target attribute (tags) by an array of strings. The idea would be that if the SS attribute contains all of the passed strings, it passes the filter. The passed strings do not need to match all of the strings within the target attribute. The more strings you pass, the more accurate your results.
// Compile tags into a list
let tagSqlValues = {};
let tagSqlStatement = query.tags.map((tag: string, index: number) => {
let tagParam = `:tag${index}`;
tagSqlValues[tagParam] = tag;
return `${tagParam} in tags`;
}).join(" and ");
// Console Logs
// tagSqlStatement = :tag0 in tags and :tag1 in tags (also tried tags contains :tag0 and tags contains :tag1)
// tagSqlValues = {":tag0":"Modern",":tag1":" Spring"}
let params = {
TableName: "Art",
FilterExpression: tagSqlStatement,
ExpressionAttributeValues: tagSqlValues,
};
let results = await this.DDB_CLIENT.scan(params).promise();
// Console Logs
// "Invalid FilterExpression: Syntax error; token: \"tags\", near: \"in tags and\""
// "Invalid FilterExpression: Syntax error; token: \"contains\", near: \"tags contains :tag0\""
I've tried several variations with IN and CONTAINS without luck. Is this possible with DynamoDB?
It looks like my CONTAINS syntax was wrong. I did a little digging and found this answer by Zanon. With a minor modification to include the and join, it seems like the filter is working as expected!
// Compile tags into a list
let tagSqlValues = {};
let tagSqlStatement = query.tags.map((tag: string, index: number) => {
let tagParam = `:tag${index}`;
tagSqlValues[tagParam] = tag;
return `contains(tags, ${tagParam})`;
}).join(" and ");

option.timeout ignored waiting for Selector.withAttribute

I have tried this every which way, but I can't get TestCafe to wait for the disabled attribute to be removed from an element.
This obviously blocks all further testing, since I need the button to be clickable before I can proceed in the flow.
fixture('create').page('locahost:3000');
test('one', async => {
const myIframe = Selector('#myIframe');
await t
.typeText('#input', 'words')
.click('#update')
.expect(myIframe.exists).ok('', { timeout: 10000 })
.switchToIframe(myIframe)
const activeStartButton = await Selector('#start').withAttribute('disabled');
await t
.expect(activeStartButton).notOk('', { timeout: 60000, allowUnawaitedPromise: true });
});
Regardless of whether I defined activeStartButton ahead of time, or add or remove await from the definition, put the selector directly in expect with or without await, separate this await block from the previous one or add it to the previous chain, TestCafe immediately throws an error atexpect(activeStartButton).notOk`
The error varies depending on my approach, but for this code:
AssertionError: start button remains disabled: expected [Function: __$$clientFunction$$] to be falsy"
Your code should look like this:
const selector = Selector('#start')
.with({visibilityCheck: true});
await t
.expect(selector.exists).ok({timeout: 10000}) // ensure the button is visible on the screen
.hover(selector) // access to the button via the mouse
.expect(selector.hasAttribute("disabled")).notOk({timeout: 10000}) // ensure the field is enabled
.click(selector);
Maybe you should also have a look to say goodbye to flakyness
This code:
const mySelector = Selector('any css selector');
await t
.expect(mySelector).notOk()
will always throw an error because the truthiness of mySelector is always true. So the above code is similar to this code:
assert(true).toBe(false).
Above mySelector is a promise object and the truthiness of a promise is always true.
Now if you write:
const mySelector = await Selector('any css selector');
await t
.expect(mySelector).notOk();
mySelector is a NodeSnaphsot object which is some sort of literal object with plenty of properties on it like:
{
textContent,
attributes,
id,
clientHeight,
...
}
The truthiness of a literal object is always true, and therefore the above
expect will still throw an error.
In fact, this problem could have been completely masked if the test code was instead:
const mySelector = await Selector('any css selector');
await t
.expect(mySelector).ok();
The above test code will always pass even if mySelector does not represent any existing element in the DOM.
Inside the expect you should assert only for a property or for a method of the Selector that returns a boolean value when using ok() or notOk().
Possible boolean properties are:
mySelector.hasChildElements
mySelector.hasChildNodes
mySelector.checked
mySelector.focused
mySelector.selected
mySelector.visible
mySelector.exists
Possible methods are:
mySelector.hasClass('className')
mySelector.hasAttribute('attributeName')
The `.withAttribute('attributeName') is just a filter method that returns a Selector object (i.e. a Promise) and the truthiness of this result is always true.
So when you are writing :
const mySelector = Selector('any css selector').withAttribute('attributeName');
it's more or less like writing this pseudo-code:
const mySelector = Selector('any css selector') // returns a collection of Selectors
.toArray() // convert it to an array
.filter((selector) => selector.hasAttribute('attributeName'))
.toPromise() // convert back to a promise object

How to create a Flow Union runtime refinement without embedding literals

Hello kind Stackoverflow folks,
I'm trying to create a function to guard off code from being executed at run-time with an incorrect Flow type present.
My understanding is that the way to do this at run-time is by refining, or checking, that the type matches what is required and using Flow to keep an eye that no cases are missed along the way.
A simple case is where I have a string input that I would like to confirm matches to a enum/Union type. I have this working as I would expect with literals e.g.
/* #flow */
type typeFooOrBaa = "foo"| "baa"
const catchType = (toCheck: string): void => {
// Working check
if (toCheck === "foo" || toCheck === "baa") {
// No Flow errors
const checkedValue: typeFooOrBaa = toCheck
// ... do something with the checkedValue
}
};
Try it over here
Naturally, I would like to avoid embedding literals.
One of the things I've tried is the equivalent object key test, which doesn't work :-( e.g.
/* #flow */
type typeFooOrBaa = "foo"| "baa"
const fooOrBaaObj = {"foo": 1, "baa": 2}
const catchType = (toCheck: string): void => {
// Non working check
if (fooOrBaaObj[toCheck]) {
/*
The next assignment generates the following Flow error
Cannot assign `toCheck` to `checkedVariable` because: Either string [1] is incompatible
with string literal `foo` [2]. Or string [1] is incompatible with string literal `baa` [3].",
"type"
*/
const checkedVariable: typeFooOrBaa = toCheck
}
};
Try it over here
Is it possible to achieve something like this without having to go down the full flow-runtime route? If so how is it best done?
Thanks for your help.
One approach that appears to works is to use the const object which defines the allowed values, to:
Generate a union type using the $keys utility.
Use that union type to create a map object where the keys are the desired input (our case strings) and the values are "maybe"s of the type that needs refining.
Here's the example from earlier reworked so that it:
Sets the type up as we'd expect to allow either "foo" or "baa" but nothing else.
Detects when a string is suitably refined so that it only contains "foo" or "baa".
Detects when a string might contain something else other than what's expected.
Credit to #vkurchatkin for his answer that helped me crack this (finally).
/* #flow */
// Example of how to persuade Flow to detect safe adequately refined usage of a Union type
// at runtime and its unsafe, inadequately refined counterparts.
const fooOrBaaObj = {foo: 'foo', baa: 'baa'}
type typeFooOrBaa = $Keys<typeof fooOrBaaObj>
// NB: $Keys used inorder for the type definition to avoid aliasing typeFooOrBaa === string
// which allows things like below to correctly spot problems.
//const testFlowSpotsBadDefition: typeFooOrBaa = "make_flow_barf"
const fooOrBaaMap: { [key: string]: ?typeFooOrBaa } = fooOrBaaObj;
// NB: Use of the "?" maybe signifier in the definition a essential to inform Flow that indexing into
// the map "might" produce a "null". Without it the subsequent correct detection of unsafe
// unrefined variables fails.
const catchType = (toCheck: string): void => {
const myValue = fooOrBaaMap[toCheck];
if (myValue) {
// Detects refined safe usage
const checkedVariable: typeFooOrBaa = myValue
}
// Uncommenting the following line correctly causes Flow to flag the unsafe type. Must have the
// "?" in the map defininiton to get Flow to spot this.
//const testFlowSpotsUnrefinedUsage: typeFooOrBaa = myValue
}
Have a play with it over here
You can type the object as {[fooOrBaa]: number}, but flow will not enforce that all members of fooOrBaa exist in the object.

Turn Undefined Property Access Into an Error

I noticed I can define a type for a function and accidentally access properties on that function
type FakeType = {}
type aFunc = string => number
const b: aFunc = () => 1
const a: FakeType = b.whatTheHeck // Flow makes "whatTheHeck" any type
Is there a way to make this an error in Flow?
I don't really know why Flow doesn't error on this automatically, but what you can do is declare aFunc as a callable object, rather than just a function, e.g.
type aFunc = {
(string): number,
};
(On flowtype.com/try)

exported typed functions that return functions should NOT require the returned function to be typed to work in other modules

For example if you are using redux-actions and have created the following module definition for it:
declare module 'redux-actions' {
declare type ActionType = string
declare type Action = {
type: ActionType,
payload?: any,
error?: bool,
meta?: any,
}
declare function createAction<T>(
type: string,
payloadCreator?: (...args: Array<T>) => any,
metaCreator?: Function
): (...args: Array<T>) => Action
}
and then you use that function to return a new function like this:
export const selectProfileTab = createAction('SELECT_PROFILE_TAB', (index: number) => {
playSound()
return { index }
})
and then in another file use it incorrectly like this:
selectorProfileTab('someString')
that won't report an error. It seems to be because flow requires annotations at the "boundaries" of modules. Am I correct?
Because the following does work:
export const selectProfileTab: (index: number) => any = createAction('SELECT_PROFILE_TAB', (index: number) => {
playSound()
return { index }
})
Notice I've annotated the returned function. The following will now produce an error:
selectProfileTab('someString')
I'm just trying to get a hold of this and verify this because it's a lot of extra "boilerplate" to annotate those returned functions, especially when it calling selectProfileTab('someString') would correctly produce an error if used in the same file. It makes you think: what's the point in creating module definitions for the redux-actions package which doesn't have them yet if it doesn't provide any/much value since you have to annotate your returned functions anyway. That's quite disappointing. Am I correct in determining that it's a module "boundaries" limitation/requirement with Flow? Are there any ways to get the desired result of not having to type returned functions that you export?

Resources