I have the following code fragment that appears to be correct, but jslint doesn't like it.
var VALID_TYPE = {
"stringType" : "string",
"arrayType" : "array",
"objectType" : "object"
},
DEFAULT_FIRST = 1, DEFAULT_LAST = 1, PRIMITIVE_TYPE = {
"stringType" : "string",
"arrayType" : "array",
"objectType" : "object",
"undefinedType" : "undefined",
"booleanType" : "boolean",
"numberType" : "number"
};
VALID_TYPE.toString = function () {
var types = [], currentType;
for (currentType in this) {
if (typeof this[currentType] === PRIMITIVE_TYPE.stringType) {
types.push(this[currentType]);
}
}
var outputString = types.join(', ');
return outputString;
};
The erroneous line is this, at the ".":
if (typeof this[currentType] === PRIMITIVE_TYPE.stringType) {
The exact text of the error is:
Expected a string and instead saw '.'.
toString() performs as expected. I can't see what I should change to avoid the error, except for placing the right side of the expression into another variable. The error is not yet described at jslinterrors.com.
As #SLaks stated in the comments, JSLint will warn when it encounters a comparison operator in which one of the operands is a typeof expression and the other operand is not a string literal.
Here's a cut down version of the code that performs this check:
function relation(s, eqeq) {
var x = infix(s, 100, function (left, that) {
// ...
if (are_similar(left, right) ||
((left.id === '(string)' || left.id === '(number)') &&
(right.id === '(string)' || right.id === '(number)'))) {
that.warn('weird_relation');
} else if (left.id === 'typeof') {
if (right.id !== '(string)') {
right.warn("expected_string_a", artifact(right));
} else if (right.string === 'undefined' || right.string === 'null') {
left.warn("unexpected_typeof_a", right.string);
}
} else if (right.id === 'typeof') {
if (left.id !== '(string)') {
left.warn("expected_string_a", artifact(left));
} else if (left.string === 'undefined' || left.string === 'null') {
right.warn("unexpected_typeof_a", left.string);
}
}
// ...
});
// ...
}
The only other time that specific warning is given is when JSLint encounters an unquoted JSON property:
{
a: 1
}
I'll get this up on http://jslinterrors.com as soon as I get a chance.
toString() performs as expected.
The code is perfectly valid, so yes it would do.
Remember that jsLint isn't looking for errors; it's looking for things it thinks are bad practice.
But those things aren't always definitively wrong in every case; often there is a legitimate use case for it, and if you've got one of those cases, then you'll still get the error, but just have to ignore it.
Lint errors should be considered as a guide rather than something to be strictly adhered to and causing build failures.
You may also want to consider using jsHint rather than jsLint. jsHint is based on jsLint, but tends to be a bit more pragmatic about what it complains about.
Hope that helps.
Related
i am trying to make a CLIish server in node.js.
but I need a way to parse a string and run a function from an object.
what I mean is... I don't want to nest a million switch statements just to have the commands I need.
using 2 other StackOverflow answers, I got 1 part done. inputs.
now i just need to figure out how to figure ou where the command stops and the input begins.
example:
inputting do say user:Yimmee msg:"well hello" "something random":yes
I need to separate do say and the inputs.
this is what i started with, but I do not know how to finish it.
function command(command, usable){
//usable is the object holding the commands that can be used.
//here I set commandMain to the part of command that is the command
/*and where commandInput is too. and I'm not forcing you,
but is preferably to be converted to an object.*/
var commandSplit = [];
do{
var match = (/[^ "]+|"([^"]*)"/gim).exec(commandMain);
if(match != null){
commandSplit.push(match[1] ? match[1] : match[0]);
}
}while (match != null);
var reach = `usable`;
commandSplit.forEach((to, nu)=>{
if(nu === commandSplit.length - 1){
reach += `["_${to}"]`;
}else{
reach += `["${to}"]`;
}
});
console.log(reach);
try{
return eval(reach)(commandInputs);
}catch(error){
return false;
}
}
Note I gave up a little, there will be some ridiculous errors.
big fat edit::::::::::::::::::::::L:::::::
idk how in the world process.argv works, and looking in one of the answers, i know how to set it.
but i am using a live websocket for this.
Unless this is an exercise, I'd strongly recommend not to implement your own command and argument parser. Use one of the existing libraries. A quick web search for "node cli library" yields a lot of results, including comparisons.
The libraries range from tiny and simple like minimist, very popular ones like yargs or commander, to heavier ones like oclif.
I'd also recommend checking the Command-line utilities section of Sindre Sorhus' Awesome Node.js list.
What you are doing is passing options and arguments to a program. You can use process.argv to get these.
It's always good to have useful error messages and command line documentation. Hence, if you're distributing to users, a more robust library for this purpose is worth an extra dependency. Widely used is yargs, see their website at https://www.npmjs.com/package/yargs for some examples.
If you want to do it using the basic process.argv, here's a solution:
This is your command in a format most people are used to: node some.js --user Yimmee --msg "well hello" --random
And the implementation
let arguments = process.argv.slice(2); // this removes `node` and the filename from arguments list
console.log(arguments)
switch (arguments[0]) { // check that `say` is the first "command"
case 'say':
let options = process.argv.slice(3); // get the stuff after `say`
let optionsObject = {} // key-value representation
if (options.indexOf("--user") != -1) { // if it exists
optionsObject.user = options[options.indexOf("--user")+1]
}
else {
// you can throw an error here
}
if (options.indexOf("--msg") != -1) { // if it exists
optionsObject.msg = options[options.indexOf("--msg")+1]
}
if (options.indexOf("--random") != -1) { // if it exists
optionsObject.random = true
}
console.log(optionsObject) // you can use optionsObject for your program
break;
default:
console.log("Invalid command");
}
EDIT: If this is happening inside the code as a function call, you can adapt above code:
function test(argsString) {
let arguments = argsString.split(/ (?=(?:(?:[^"]*"){2})*[^"]*$)/); // split the string into an array at the spaces
// ^ regex from https://stackoverflow.com/questions/23582276/
console.log(arguments)
switch (arguments[0]) { // check that `say` is the first "command"
case 'say':
let options = arguments.slice(1); // get the stuff after `say`
let optionsObject = {} // key-value representation
if (options.indexOf("--user") != -1) { // if it exists
optionsObject.user = options[options.indexOf("--user") + 1]
}
else {
// you can throw an error here
}
if (options.indexOf("--msg") != -1) { // if it exists
optionsObject.msg = options[options.indexOf("--msg") + 1]
}
if (options.indexOf("--random") != -1) { // if it exists
optionsObject.random = true
}
console.log(optionsObject) // you can use optionsObject for your program
break;
default:
console.log("Invalid command");
}
}
I'm doing a PWA quiz application using React.js and I've met the following problematic:
I can get questions objects with only one answer, and some with multiple.
In the case there is only one possible answer, I want to force the user to only have one possibility.
To do that, I made the following algorithm:
clickOnChoice = (key) => {
if (this.state && this.state.correctAnswers) {
let newChoices = INITIAL_CHOICES; // {}
if (this.state.multiChoice) {
console.log("this.state.multiChoice:", this.state.multiChoice); // this.state.multiChoice: false ???
newChoices = JSON.parse(JSON.stringify(this.state.choices)); // {answer_b: 1}
}
newChoices[key] = 1 - (newChoices[key] | 0); // {answer_b: 1, answer_a: 1}
this.setState({
choices: newChoices
}, this.updateNextButtonState);
}
}
However the execution seems to ignore the condition if (this.state.multiChoice).
What am I missing?
Maybe I need a cup of coffee... ☕
Anyway, thanks in advance!
It is more than likely you are trying to checking a string of 'false' rather than an actual boolean value.
you can check that the string is the expected boolean if (this.state.multiChoice === 'true') or change the value of the state property to true || false
I already searched for similar issues but I didn't find anything that could help me yet.
I'm trying to reach a picture path (using JSON format) depending on the material type of the picked element. Actually, my code is built like this:
if (globalData.Material.Mat_type == "OSCILLOSCOPE") {
var picture = (globalData.Material.Oscilloscope.picture);
}
if (globalData.Material.Mat_type == "ALIMENTATION") {
var picture = (globalData.Material.Alim.picture);
}
But not optimized at all, so Im trying to make it this way :
var mat_type = (globalData.Material.Mat_type);
var picture = (globalData.Material[mat_type].picture);
But it doesn't work... Got some exception:
TypeError : globalData.Material[mat_type] is undefined.
I already tried a lot of things, have you got any idea? Thanks!
I outlined the issue with character case in the comment under the question, so presumably adjusting value of globalData.Material.Mat_type could do the trick:
var mat_type =
globalData.Material.Mat_type.charAt(0).toUpperCase() +
globalData.Material.Mat_type.substr(1).toLowerCase();
I can also see that this general rule may not be applicable in all cases. If it's not a typo, it won't work for the second case where Mat_type == "ALIMENTATION", because then you try to access Alim property of Material instead of Alimentation. In this case you could access property by prefix:
function pictureOf(material) {
if (!material || !String(material.Mat_type)) {
return null;
}
let mat_type = String(material.Mat_type).toUpperCase();
for (var propertyName in material) {
if (mat_type.startsWith(propertyName.toUpperCase())) {
return material[propertyName].picture || null;
}
}
return null;
}
console.log(pictureOf({
Mat_type: "OSCILLOSCOPE",
Oscilloscope: {
picture: "picture of oscilloscope"
}
}));
console.log(pictureOf({
Mat_type: "ALIMENTATION",
Alim: {
picture: "picture of alimentation"
}
}));
But this kind of approach can be error prone, if multiple properties share the same prefix. There's also a hidden issue with case-insensitive prefix matching in case you use some special unicode characters in property names. Lastly this method is not efficient, because it has to iterate over all properties of the object (worst case scenario). It can be replaced with much safer property mapping:
const matTypeMapping = {
"ALIMENTATION": "Alim"
};
function pictureOf(material) {
if (!material || !String(material.Mat_type)) {
return null;
}
let matType = String(material.Mat_type);
// find property mapping or apply general rule, if mapping not defined
let propertyName = matTypeMapping[matType] ||
matType.charAt(0).toUpperCase() + matType.substr(1).toLowerCase();
return material[propertyName].picture || null;
}
console.log(pictureOf({
Mat_type: "OSCILLOSCOPE",
Oscilloscope: {
picture: "picture of oscilloscope"
}
}));
console.log(pictureOf({
Mat_type: "ALIMENTATION",
Alim: {
picture: "picture of alimentation"
}
}));
NB: To avoid headaches, maybe you should prefer strict equality operator over loose equality operator.
Problem Solved
Peter Wolf was right ! It was a case-sensitive issue
I actually don't know how to promote his comment, sorry for this..
Anyway, thank you guys !
var mat_type = (globalData.Material.Mat_type);
if(mat_type!==undefined)
var picture = (globalData.Material[mat_type].picture)
Just do an existential check before accessing the value, for keys that may not be present.
I'm getting the below error when I check the length of an array. What would be the correct approach?
main.js
if (drugPrice.mailPrice.rejectMessage.length !== 0 && Array.isArray(drugPrice.mailPrice.rejectMessage)) {
//code goes here
}
Error
TypeError: Cannot read property 'length' of undefined
Try swapping the order of the checks:
if (Array.isArray(drugPrice.mailPrice.rejectMessage) && drugPrice.mailPrice.rejectMessage.length !== 0) {
code goes here
}
Validate your data, swapping the condition may help but it won't prevent, some errors from happeing. For example Array.isArray(drugPrice.mailPrice.rejectMessage) will throw an error if drugPrice.mailPrice is undefined.
if (drugPrice.mailPrice
&& drugPrice.mailPrice.rejectMessage
&& drugPrice.mailPrice.rejectMessage.length !== 0
&& Array.isArray(drugPrice.mailPrice.rejectMessage)) {
// code goes here
}
var drugPrice = { mailPrice: { rejectMessage: {} } };
if (drugPrice.mailPrice
&& drugPrice.mailPrice.rejectMessage
&& drugPrice.mailPrice.rejectMessage.length !== 0
&& Array.isArray(drugPrice.mailPrice.rejectMessage)) {
console.log('success');
} else {
console.log('fail')
}
NOTE
Always validate your data. Don't assume that you'll always get the right data. When working with objects always validate them, as doing data.name, can break your app, if data is null or undefined. for example, given the following object.
const drugPrice = { mailPrice: null };
doing, throws an error.
const drugPrice = { mailPrice: null };
// throws an error, Cannot read property 'rejectMessage' of undefined
if (Array.isArray(drugPrice.mailPrice.rejectMessage)) {
}
to prevent that from happening, we need to check if the propery exists, like the following.
const drugPrice = { mailPrice: null };
console.log(drugPrice.mailPrice && Array.isArray(drugPrice.mailPrice.rejectMessage) || 'Price is null or undefined')
You do not really need to actually do the .length !== 0. You can simply do:
if (Array.isArray(A.B.C) && A.B.C.length) { // <-- order is important here
//...
}
.length would be evaluated as a boolean and it will give you the same result as checking with !==0
That being said however your paths are quite long so you probably would want to make sure they are valid. Meaning if drugPrice or mailPrice are falsey you would have an issue. So usually you would want to check on them as well. Since your question was about the array part I will skip those but just as FYI.
You can build your own path checker or if you use libraries like lodash/underscore etc they always have a handy get/has functions to check like this (with lodash):
if (_.has(drugPrice, 'mailPrice.rejectMessage.length'))
//...
}
Obviously do not use those libraries just for that but if you already have them those methods are quite handy. You can simply check each of the paths as well via:
if (A && A.B && Array.isArray(A.B.C) && A.B.C.length) {
//...
}
It just gets tedious if you have long object paths etc.
The problem in your code is that javascript checks array length before checking if the array is the type of the array. You should change the order in the if statement.
You can try with:
if (myArr && Array.isArray(myArr) && myArr.length !== 0) {
// your code
}
Now the code is executed in the right order.
The first condition checks if myArr is defined,
The second condition checks if myArr is the type of Array, you can also do this way:
if (myArr && myArr.push && myArr.length !== 0) {
// your code
}
The third condition checks if myArr is not empty.
I have this condition which verifies the same property labelKey of an object projectType and return of different value according to the value of the property
checkProjectType () {
if (this.projectType.labelKey === 'project_type.rent') {
return 'geographical_area'
} else if (this.projectType.labelKey === 'project_type.buying') {
return 'geographical_area'
} else {
return 'address'
}
}
since there is too much resemblance in the condition how I refactored / optimized the condition with a simplified write using Lodash or ECMAScript 2015 for example ?
You can reduce this to less conditions as per your code.
checkProjectType () {
var labelKey = this.projectType.labelKey;
if (labelKey === 'project_type.rent' || labelKey === 'project_type.buying') {
return 'geographical_area';
}
return 'address';
}
Not sure what you want to do here with lodash
I also don't like if-else-if… chains, so prefer more readable variant.
function checkProjectType() {
const defaultType = 'address';
const key = this.projectType.labelKey;
let map = {
'project_type.rent': 'geographical_area',
'project_type.buying': 'geographical_area'
};
return map[key] || defaultType;
}
map can be defined somewhere else.
Setting an if do X else if do X else do Y is wrong to me, you can simplify that in a single line : if (this.projectType.labelKey === 'project_type.rent' || this.projectType.labelKey === 'project_type.buying') would be easier to read already.
One alternative way this could be written is using a switch statement:
switch (this.projectType.labelKey) {
case 'project_type.rent':
case 'project_type.buying':
return 'geographical_area';
default:
return 'address';
}
But one might argue it's a bit overkill in this case. Lodash or ECMAScript 2015 isn't going to do anything for you here.
You can check if the project type is included in an array of types, and use a ternary to select the response:
checkProjectType() {
return ['project_type.rent', 'project_type.buying'].includes(this.projectType) ? 'geographical_area' : 'address';
}
If the types that produce geographical_area, you can refactored them out of the method (and the object/class):
const geoTypes = ['project_type.rent', 'project_type.buying'];
checkProjectType() {
return geoTypes.includes(this.projectType) ? 'geographical_area' : 'address';
}