Cannot pass array of string to function - node.js

I have this function:
function proc(unames: Array<string>){}
I try to pass it this:
import _ = require('lodash');
const usernames = _.flattenDeep([unames]).filter(function (item, index, arr) {
return item && arr.indexOf(item) === index;
});
const recipient = 'foobarbaz';
proc(usernames.concat(recipient));
I get this error:
Does anyone know how to mitigate this?
I tried this, and I get an even longer and crazier error:
function proc(unames: Array<string | ReadonlyArray<string>>){}
however, this made the error go away:
function proc(unames: Array<string | ReadonlyArray<any>>){}
not really sure what's going on.

The warning seems to be referring to the use of .concat() rather than proc().
When called on an Array, such as usernames, TypeScript is validating that the arguments given to .concat() are also Arrays.
To resolve the warning, you have a few options:
Since you're using Lodash, its own _.concat() allows for appending individual values, and TypeScript's validation should be aware of that:
const recipient = 'foobarbaz';
proc(_.concat(usernames, recipient));
Define recipient as an Array or wrap it when calling .concat():
const recipient = [ 'foobarbaz' ];
proc(usernames.concat(recipient));
const recipient = 'foobarbaz';
proc(usernames.concat( [recipient] ));
You may also be able to configure TypeScript to validate for a later version of ECMAScript. Between 5.1 and 2015 (6th edition) of the standard, the behavior of the built-in .concat() was changed to support individual values (by detecting spreadable).
For now, TypeScript is validating .concat() for 5.1 or older.

Related

populate object properties using lambda expression in typescript

Newbie Alert! I feel silly asking this question but I need someone to teach me the correct syntax.
I have code that looks like this:
let thing: INewThing;
thing.personId = another.personId;
thing.address = another.work.address;
thing.greades = another.subjectInfo.grades;
thing.isCurrent = another.student.isCurrent;
I know it can be written cleaner. I want to use a lamda expression, something like this:
let thing: INewThing => {
personId = another.personId,
address = another.work.address,
grades = another.subjectInfo.grades,
isCurrent = another.student.isCurrent
} as IThingUpdate;
I have looked and looked for an example. I have yet to find one that works for me. It's just syntax but no matter what I try it doesn't work.
You're just looking to create a new object, which is a pretty different thing from a "lambda" (function). Just declare the object. You don't need a function.
const thing = {
personId: another.personId,
address: another.work.address,
// use the correct spelling below - no 'greades'
grades: another.subjectInfo.grades,
isCurrent: another.student.isCurrent,
};
If the another is typed properly, that should be sufficient.
If the another object had more properties using the same path, another option would be to destructure those properties out, then declare the object with shorthand, eg:
const originalObj = { prop: 'val', nested: { foo: 'foo', bar: 'bar', unwanted: 'unwanted' } };
const { foo, bar } = originalObj.nested;
const thing = { foo, bar };
Destructuring like this, without putting the values into differently-named properties, helps reduce typos - if a property starts out, for example, as someLongPropertyName, putting it into a standalone identifier someLongPropertyName and then constructing an object with shorthand syntax ensures that the new object also has the exact property name someLongPropertyName (and not, for example, someLongPRopertyName - which isn't that uncommon of a mistake when using the more traditional object declaration format).
But since all the paths in your another object are different, this approach wouldn't work well in this particular situation.

Jest Expected and receive are identical

Is there a reason why Jest would see this as a not identical when both seem exactly the same?
So here is the code I am using to do the test, it's basically just a function that calls for an event emiter, in the event emiter if the date is invalid, I let it as is :
const datepickerComponent: Datepicker = new Datepicker();
const mockEvent = {
target: {
classList: {
remove: jest.fn(),
add: jest.fn(),
},
value: '01-01-197',
},
} as unknown as InputEvent;
datepickerComponent.onInput(mockEvent);
const emitMock: jest.Mock = jest.fn();
datepickerComponent.dsdDatepickerInputChange = { emit: emitMock } as unknown as EventEmitter<dsdDatepickerInputChangeEvent>;
// when
datepickerComponent.onInput(mockEvent);
const dateValue = new Date('197-01-01T00:00:00');
// then
expect(emitMock).toHaveBeenCalledWith({ value: '197-01-01', valueAsDate: dateValue });
The reason why you are observing this error is because whilst Date { NaN } values look the same, they actually refer to different object instances and cannot be traversed for equality any further, hence the actual error should be the following:
Expected: {"value": "197-01-01", "valueAsDate": Date { NaN }}
Received: serializes to the same string
(To reproduce this error - create two new dates using new Date('197-01-01T00:00:00') and pass them into .equals())
To get past this error, all you need to do is to simply refactor your .toHaveBeenCalledWith test into the following:
const calledWithArg = emitMock.mock.calls[0][0];
expect(JSON.stringify(calledWithArg)).toEqual(JSON.stringify({ value: '197-01-01', valueAsDate: dateValue }));
The reason why .toHaveBeenCalledWith does not work is because it does not allow us to reshape the argument object before a comparison (in our case we need to stringify it), hence we can alternatively extract the argument that the mock was called with via .mock.calls[0][0], stringify it and then compare it to the stringified version of the expected object.
Reference of valueAsDate is different. But Jest doc affirm it uses .toEqual for comparison. That means it try to deeply compare two new Date. This is a bad idea, you don't control the Date class, and there are probably some moving parts inside.
To loose match a value, you can check this issue: https://stackoverflow.com/a/55569458/7696155

Enum attribute in lit/lit-element

We are trying to build a component with a property variant that should only be set to "primary" or "secondary" (enum). Currently, we are just declaring the attribute as a String, but we were wondering if there is a better way for handling enums? For example, should we validate somehow that the current value is part of the enum? Should we throw an error if not?
I asked this question on Slack and the answers I got lean towards declaring the property as String and use hasChanged() to display a warning in the console if the property value is invalid.
Standard HTML elements accept any string as attribute values and don't throw exceptions, so web components should probably behave the same way.
This all sounds reasonable to me.
If you're using TypeScript I'd recommend just using strings. You can use export type MyEnum = 'primary' | 'secondary' to declare it and then use #property() fooBar: MyEnum to get build time checking. You can use #ts-check to do this in plain JS with #type MyEnum too.
This works well if the enums are for component options or that map to server-side enums that will get validated again.
However, if you want to validate user input into enums or loop through them a lot this is less good. As the JS runs it has no visibility of the type. You need an object dictionary, something like:
const MyEnum = Object.freeze({
primary: 'primary',
secondary: 'secondary'
});
// Enforce type in TS
const value: keyof MyEnum;
// Validate
const validated = MyEnum[input.toLower()];
// Loop
for(const enumVal of Object.keys(MyEnum)) ...
// Or Convert to a different value type
const MyEnum = Object.freeze({
primary: 1,
secondary: 2
});
These are somewhat idiosyncratic. Again, if you're using TypeScript it has an enum keyword that compiles to something like this and I'd use that rather than rolling your own. Strings are the better option unless you need to validate, loop or convert the values.

How to properly call multiple environment variables within a variable definition in Node

I have two environment variables that I have saved in my local local.env.js file. I'd like to use them in place of the username and password in the following code:
var admins = {
'frank': { password: 'mypassword' },
};
When I use the code below, I get an error (Unexpected token .):
var admins = {
process.env.BASIC_AUTH_USER: {password: process.env.BASIC_AUTH_PASSWORD},
};
Any suggestions on how to do this correctly?
In ES5 and earlier, a static object declaration cannot use a "computed" value for a property name - it must be a string literal. So, you have to use an actual line of code to assign the property using the obj[computedPropName] = value; syntax. In your specific case, that would look like this:
var admins = {};
admins[process.env.BASIC_AUTH_USER] = {password: process.env.BASIC_AUTH_PASSWORD};
In ES6, you can use a computed value in a static declaration if you enclose it in the array syntax like this:
var admins = {[process.env.BASIC_AUTH_USER]: {password: process.env.BASIC_AUTH_PASSWORD}};
See this description of new ES6 features and this MDN reference for some further description of this feature.
Portions of ES6 are available in some of the latest version of browsers, in runtime environments like node.js and, of course, you can use transpilers to code in ES6, but transpile to ES5 compatible code for many features. We are, of course, a ways away from being able to rely on native ES6 support for general cross browser use (thus the interest in transpilers now).
You can use array-like-notation, like so:
var admins = {};
admins[process.env.BASIC_AUTH_USER] = {
password: process.env.BASIC_AUTH_PASSWORD
};
If BASIC_AUTH_USER is "Frank" and BASIC_AUTH_PASSWORD is "potatosalad" then you will end up with an object like this:
admins: {
Frank: {
password: 'potatosalad'
}
}
Try this
admins = {};
admins[process.env.BASIC_AUTH_USER] = {password: process.env.BASIC_AUTH_PASSWORD};

Query value gets quoted automatically before sending it to MongoDB?

The following is confusing me a lot. I have been spending quite a bit of time trying to understand why collection.find() doesn't work with regex passed as an object. The regex match is coming over HTTP wrapped in the body of a POST request. Then I try to gather the query (in string format) and perform the query. The problem seems to be that unless the regex is written inside Node without quotes, it won't work. That is, it must be a literal without quotes.
For example, the following works fine:
var query1 = {
company: {
'$regex': /goog/
}
};
collection.find(query1, {}).toArray(function (err, docs) {
// Got results back. Awesome.
});
However, if the data comes wrapped in an object, it doesn't return anything. I suspect it's because the value gets quoted behind the scenes (i.e. "/goog/"):
// Assume
var query2 = {
company: {
'$regex': query.company
}
};
collection.find(query2, {}).toArray(function (err, docs) {
// Got nothing back.
});
I have tested it with the mongo shell and I can confirm the following:
// Returns 5 results
db.getCollection("contacts").find( { "company": /goog/ } )
// Doesn't match anything
db.getCollection("contacts").find( { "company": "/goog/" } )
Furthermore, I just discovered the following: if I write the value with quotes
// Works fine
var companyRegex = {'$regex': /goog/};
var query3 = {
company: companyRegex
};
So technically, a "literal" regex without quotes wrapped in an object works fine. But if it's a string, it won't work. Even after trying to replace the double-quotes and single-quotes with nothing (i.e. essentially removing them.)
Any idea how can I get the regex match be passed verbatim to find()? I've researched it, finding lots of potential solutions, alas it's not working for me.
Thanks in advance!
Let me focus on one line of your post. This is where the problem might be:
The regex match is coming over HTTP wrapped in the body of a POST request.
This seems problematic because:
The only structures that survive serialization between client/server are:
boolean
number
string
null *
objects and arrays containing these basic types
objects and arrays containing object and arrays [of more obj/array] of these basic types
Regexp, Date, Function, and a host of others require reconstruction, which means
passing a string or pair of strings for the match and option components of the Regexp and running Regexp() on the receiving end to reconstruct.
Regexp gets a bit messy because Regexp.toString() and Regexp() do not appear to be inverses of each others: /someMatch/.toString() is "/someMatch/" but RegExp("/someMatch/") is //someMatch// and what was needed instead to rebuild the regexp was just RegExp("someMatch"), which is /someMatch/. I hope this helps.
JSON.stringify(/someMatch/) is {} (at least on Chrome).
So instead of trying to build a general transport, I recommend re instantiating a particular field as a regexp.
* Irrelevant note: (null is fine but undefined is peculiar. JSON won't stringify undefineds in objects and turns undefined into null in Arrays. I recognize this isn't part of your problem, just trying to be complete in describing what can be serialized.)

Resources