I'm trying to learn how to use flow, but I'm stuck with an error message that makes no sense to me.
This is my code:
import moment from "moment-timezone";
import type MomentType from "moment-timezone";
type filtersType = {
page?: number,
ccaa?: string,
province?: string,
date_from?: MomentType,
date_to?: MomentType,
distance_from?: number,
distance_to?: number,
category?: Array<string>,
};
const _parseValue = (value: mixed): string => {
if (Array.isArray(value)) return value.join(",");
else if (moment.isMoment(value)) return value.toISOString();
else if (typeof value === "number") return value.toString();
else if (typeof value === "string") return value;
throw new Error("Filter value type is not valid!!");
};
const _generateQueryItem = (key: string, value: mixed): string => {
return `${key}=${_parseValue(value)}`;
};
const _generateQuery = (filters: filtersType): string => {
return Object.entries(filters)
.map(([key, value]) => _generateQueryItem(key, value))
.join("&");
};
It keeps complaining about this, on line 33:
Cannot call value.toISOString because property toISOString is missing in mixed [1].
[1] 30│ const _parseValue = (value: mixed): string => {
31│ console.log(moment.isDate(value));
32│ if (Array.isArray(value)) return value.join(",");
33│ else if (moment.isMoment(value)) return value.toISOString();
34│ else if (typeof value === "number") return value.toString();
35│ else if (typeof value === "string") return value;
36│ throw new Error("Filter value type is not valid!!");
I always though that mixed types includes everything so, it shouldn't complain about any method missing, I'm not sure on whats going on, I tried to change the args types to:
const _parseValue = (value: mixed | MomentType): string => {
and:
const _generateQueryItem = (key: string, value: mixed | MomentType): string => {
but I still get the same error.
I need a way to make flow know that value has toISOString method but I run out of ideas for today. How do I do that?
(I already have installed moment types with flow-typed btw)
Ah, type refinement.
So you're trying to refine the type of value. You're doing this:
if (typeof value === "number") return value.toString();
which checks if value is a number. Flow understands this and refines value to a number so you can call toString on it.
You're doing this:
if (Array.isArray(value)) return value.join(",");
which checks if value is an array. Flow understands this and refines value to an array so you call join on it.
You're doing this:
if (moment.isMoment(value)) return value.toISOString();
which checks if value is an instance of a moment object. Flow does not understand this as a refinement. Therefore, your type is not being refined so when you attempt to call toISOString flow tells you that your method doesn't exist on mixed.
Flow only supports a small but somewhat powerful set of refinement operations. It has no idea that isMoment is even intended as a refinement. The only values flow can refine from mixed are really plain old data values, numbers, strings, arrays, objects, it's impossible to refine from mixed to MomentType.
So first of all, let's stop relying on mixed. If you can only possibly receive a small subset of types as parseable values, a union will serve you better, and it will explicitly add MomentType as a possibility for refinement:
type Parseable = number | string | string[] | MomentType;
Then there's the problem of our refinement for a moment object. Well, we have two possibilities. If we are really only concerned with these types, meaning we are totally confident these are the only things we will ever get, then we don't even have to explicitly refine to a moment object:
const _parseValue = (value: Parseable): string => {
if (Array.isArray(value)) return value.join(",");
else if (typeof value === "number") return value.toString();
else if (typeof value === "string") return value;
return value.toISOString(); // by process of elimination, this is a moment
};
Flow is smart enough to know that we have already handled all the other cases, so if we reach the end of the function then by process of elimination value must be a moment.
So, should we rely on this behavior? Well, it depends. Is arbitrary data from an unreliable API going to be passed into _parseValue? Or well-typed data from reliable places in our flow-typed codebase? If we don't need to worry too much about spurious data, then sure, rely on the process of elimination. Otherwise, we should probably hold onto your original error case. But that means we'll have to explicitly refine to our moment type:
const _parseValue = (value: Parseable): string => {
if (Array.isArray(value)) return value.join(",");
else if (typeof value === "number") return value.toString();
else if (typeof value === "string") return value;
// let's be extra careful
else if (typeof value === 'object' && value.isMoment && value.toISOString) return value.toISOString();
throw new Error("Filter value type is not valid!!");
};
Flow would accept our check typeof value === 'object' as enough to distinguish value from the other entries in Parseable and ensure that it is a MomentType, but as our data source might be dubious, let's also check for value.isMoment (a property on all moment objects defined in the flow-typed libdef for moment). This should prove beyond a reasonable doubt that this is, in fact, a moment object. One strategy would be to just duck-type any object that we get as having value.toISOString and go ahead and call it.
Related
I know this seems like an odd problem, but at the moment I'm creating a webserver to save data to a mongodb database using mongoose. To use that, I need to create a model with a schema which basically defines the values I can search to, as well as the type they should be (String, Number, etc.)
As I said before, since it's a webserver, I'd like to make it so the webserver will create the model and the schema, with a body object in the request. However, there is an issue. I can only pass the type (String, Number, etc.) as a string. I've come up with a solution to parse the value into a type, that being the code below
getTypeByString: function (string) {
if (string == 'String') {
return String
}
if (string == 'Number') {
return Number
}
if (string == 'Array') {
return Array
}
if (string == 'Object') {
return Object
}
}
However, I feel like there is probably a more simple solution. If there is or isn't, please let me know! I'd like to release my code on GitHub eventually, and I'd like it to be as dynamic and simple as possible. Thank you in advance
String, Number, Array and Object are also properties of the global variable (or window variable if you are in a browser).
You can check it yourself by evaluating:
global['String'] === String // true
For this reason, you can just use the string to lookup the type within the global object, and return it:
getTypeByString: function (string) {
if (['String', 'Number', 'Array', 'Object'].includes(string)) // be sure that you are actually returning a type
return global[string];
return null;
In regards to getting a defined type from a string you can use an object to define the types and use the string as the key.
const DefinedTypes = {
String: String,
Number: Number,
Array: Array,
Object: Object,
CustomType: classOfCustomType
};
Using it.
const typeAsString = 'String';
const type = DefinedTypes[typeAsString];
You could use a switch statement to make the code a bit simpler:
getTypeByString: function(stype){
switch(stype){
case "String": return String;
case "Number": return Number;
case "Array": return Array;
case "Object" : return Object;
default: return null; // or throw an error
};
};
So basically i want to check if my data (which is in JSON Format) has a value which is a primitive. So let's take an Example: I get data that looks like this: {name: Artikel, value: {"ArtNr": 1234}} and i want to check if 1234 is primitive or not. I also want to differentiate if the result is an Array with Primitives in it or an Object. Is that possible?
function isObjectContainingPrimitiveValues(test) {
let values = Object.values(test);
for (let i of values) {
console.log(i);
return (typeof i === 'string' || typeof i === 'number' || typeof i === 'boolean' || typeof i === null || typeof i === undefined);
}
}
UPDATE
So with the awesome help of MaxK i have built a isResultContainingPrimitiveValues() Function which checks my data for Primitive/ Primitive Arrays and or Objects. The following part is the trickiest at least with my understanding. The following Example will hopefully help you understand my problems better.
So my let namesSplit = treeNode.name.split('.'); variable splits the data it gets and has as a result of nameSplit : Artikel,Artnr. Next i defined a key variable let key = namesSplit[0]; which has key : Artikel as a result. Than i define a contextEntry variable let contextEntry = exprData.contextEntry.find(_x => _x.name === key); and has contextEntry : {"name":"Artikel","value":{"ArtNr":123}} as a result. Now i want to check: if there's another split namesSplit.length > 1 check isResultContainingPrimitiveValues(). If it is primitive, throw an error, if it is an object -> get values from it and if it is an array -> get values form there. I know it's a lot but from all the confusing stuff i can't seem to think clear, so i appreciate every help i can get.
You are returning from your function on the first iteration. You should only return false if you found an non-primitive and if you were able to loop over all values you can return true because all values are primitives:
function isObjectContainingPrimitiveValues(testObj) {
let values = Object.values(testObj);
for(let i of values){
if (typeof i === 'object') {
return false;
}
}
return true;
};
Update:
After reading your comment i changed the code to check for arrays with primitives as well. The idea is, to create a new function which only checks if a single value is a primitive.Now if we find an array, we can simply check - with the help
of the arrays some function - if some element, inside the array is not primitive. If so return false,otherwise we do the same checks as before:
function isObjectContainingPrimitiveValues(testObj) {
let values = Object.values(testObj);
for (let i of values) {
if (Array.isArray(i)) {
if (i.some(val => !isPrimitive(val)))
return false;
} else {
if (!isPrimitive(i))
return false;
}
}
return true;
};
function isPrimitive(test) {
return typeof test !== 'object'
}
Array and object types all return a 'typeof' 'object'. so you can check against an object instead of checking against multiple conditions.
So the return statement will be:
return (typeof i === 'object').
Number, string, undefined, null will all return false on the statement above.
We have two potential values:
const value = `Hello World`;
and
const value = {message: 'Hello World'};
What I'm trying to do is a conditional where
if(is template literal) {
// Some code
} else {
// Some code
}
I've tried using
if(String.raw(value)){
} else {
}
But the object throws a type error. Does anyone have an easy reliable way to detect template literals in javascript?
Edit: Here is some further clarification for those who are asking.
const exampleFunc = (value) => {
// If I pass a string or template literal return as is
if (String.raw({raw: value})) return value;
const css = arr(styles).reduce((acc, obj) => {
// Passing an object handles a more complicated set of operations
}
}
Before I was using
if (String.raw(value)) return value;
And what was happening here is it was giving me a type error rather than handing me null. By passing {raw: value} to String.raw instead solved the problem and I can now detect whether its a string/template literal or an object.
The clarifications to why I'm doing it this way is a longer story that has to do with React & styled components.
There is no difference between using a string literal and a template literal (except, obviously, on the source code level).
But your first value is a string, while the second one is an object, and those are easy to distinguish:
if (typeof value == "string") {
console.log(value)
} else if (typeof value == "object" && typeof value.message == "string") {
console.log(value.message)
}
I have a an object jsonRes[0] containing values which need to be removed based on a condition. The following works to remove null, missing values and those equal to zero in the stringified object:
function replacer(key, value) {
// Filtering out properties
if (value === null || value === 0 || value === "") {
return undefined;
}
return value;
}
JSON.stringify(jsonRes[0], replacer, "\t")
However, when I add a condition using the the includes method, I receive an error:
function replacer(key, value) {
// Filtering out properties
if (value === null || value === 0 || value === "" || value.includes("$")) {
return undefined;
}
return value;
}
Uncaught TypeError: value.includes is not a function
Why is this the case and is there a workaround?
You can use String.indexOf() instead of String.includes, As it is available in ES6 and not supported in IE at all.
typeof value == "string" && value.indexOf('$') > -1
Also note if value is not string type it will still raise an error boolean, Number doesn't the the method. You can use typeof to validate whether value is a string.
The .includes() API is part of the String and Array data type.
So what the error is trying to tell you is that the value for variable value, e.g. an integer or object, does not have the property .includes.
You could do checks like
typeof a_string === 'string'
an_array instanceof Array
before the .includes() api to prevent this.
Obviously this will make your if statement rather ugly due to the number of checks you have.
Based on the way your code is written I suspect you are more interested in checking "String" than array. So becareful of arrays. Your code may not work properly if it is array.
Anyway here is a refractored version of your code.
function replacer(key, value) {
// Filtering out properties
if (!value || typeof value === "string" && value.includes("$")) {
return undefined;
}
return value;
}
console.log("NULL returns:" + replacer('test', null));
console.log("$Test returns:" + replacer('test', '$test'));
console.log("Blah returns:" + replacer('test', 'Blah'));
Just one more possibility: Maybe your value is not a string type object.
(typeof(value) == "string" && value.includes("$"))
I solved this error, which I was getting when applying "includes" to a "window.location" value, by appending ".toString();"
var requestUrl = window.location.toString();
if (requestUrl.includes(urlBase + "#")) {
...
I actually am not sure what type of the variable named value is, but anyway, Array.prototype.includes and String.prototype.includes are only available in ES6. You need to use babel-polyfill or any other bundling modules like rollup.js, webpack with babel or something like that to use includes function.
I just start learning JavaScript and I faced with problem: I don't know how to check what exactly I can do with my variables (for example, how I can manage string or array). In Python there are very useful methods dir() and help() that allow user to get list of all applicable methods and find out how to use them:
>>>my_number = 1
>>>dir(my_number)
This will return
['bit_length', 'conjugate', 'denominator', 'from_bytes', 'imag', 'numerator', 'real', 'to_bytes']
the list of methods I can apply to my_number variable
Then I can get description of each method:
>>>help(my_number.real)
Help on int object:
class int(object)
| int(x=0) -> integer
| int(x, base=10) -> integer
|
| Convert a number or string to an integer, or return 0 if no arguments
| are given. If x is a number, return x.__int__(). For floating point
| numbers, this truncates towards zero...
So is there any similar function in JavaScript so I can call it like console.log(getAllMethodsFor(myNumber)) ? This could significantly simplify process of language learning...
you can get the properties of a variable and check if the typeof property is a function
function getAllFunctions(myvar)
{
var allMethods = [];
for( var property in myvar)
{
if (typeof myvar[property] == "function")
{
allMethods.push(property);
}
}
return allMethods ;
}
Nothing built-in, but easy to write:
function dir(obj) {
if(obj === null)
return [];
var uniq = a => a.filter((x, i) => a.indexOf(x) === i);
return uniq(dir(Object.getPrototypeOf(obj)).concat(
Object.getOwnPropertyNames(obj).filter(p => typeof obj[p] === 'function')
));
}
document.write(dir(1))
document.write("<hr>");
document.write(dir("abc"))
Regarding help, there's no such thing either, but typing mdn <method name> in google usually does the trick.
There is no such thing by default, but you could write one simply by iterating the properties of the given object:
for (var prop in obj) {
if (typeof obj[prop] == 'function') {
console.log(prop);
}
}
Having said that, that's still missing the integrated docstrings for help(). Typically in Javascript development you're reading the (hopefully) accompanying documentation and/or use an interactive console like Chrome's Web Inspector to inspect objects in a more GUI-like manner.