I don't understand what's going on this syntax javascript [duplicate] - javascript

This question already has answers here:
Javascript object bracket notation ({ Navigation } =) on left side of assign
(5 answers)
Closed 5 years ago.
How gonna assign in const { Types, Creators } in the below code I mean what Types gonna hold and what Creators gonna hold.
const { Types, Creators } = createActions({
userRequest: ['username'],
userSuccess: ['avatar'],
userFailure: null
})
var createActions = (function (config, options) {
if (R.isNil(config)) {
throw new Error('an object is required to setup types and creators');
}
if (R.isEmpty(config)) {
throw new Error('empty objects are not supported');
}
return {
Types: convertToTypes(config, options),
Creators: convertToCreators(config, options)
};
})

The syntax is object destructuring assignment. Types and Creators will be defined as the Types and Creators properties returned from the object returned at createActions() call. For example
const {Types, Creators} = (() => {
return {Types:0, Creators:1}
})();
console.log(Types, Creators)

This is called a destructuring assignment, It looks at the returned object and assigns the correct key to the variable. Think of it as shorthand for:
const createActions = (function (config, options) {
if (R.isNil(config)) {
throw new Error('an object is required to setup types and creators');
}
if (R.isEmpty(config)) {
throw new Error('empty objects are not supported');
}
return {
Types: convertToTypes(config, options),
Creators: convertToCreators(config, options)
};
})
let results = createActions({
userRequest: ['username'],
userSuccess: ['avatar'],
userFailure: null
}),
Types = results.Types,
Creators = results.Creators;

Related

Can Constructor functions inherit an interface in Typescript?

I have a JS constructor function written in Typescript and i access this constructor function from other code like this
const ls = new LocalStorage<ICountryHolidayData>()
let strdVal = ls.getItem(constants.localStorageKey).storedValue
Though the code works fine but i do not get any intellisense..neither for ls.getItem nor for the storedValue property.
I think its because constructor functions can't inherit interface like classes do in typescript?
Otherwise can some help me understand how to do this, so that i can get intellisense for both values.
PS: i know the easier way is to use classes but i am curious on how to do it via constructor functions
export interface ILocalStorgaeReturnValue<Type>{
storedValue: Type,
date: Date
}
export interface ILocalStorage<Type extends {}>{
setItem(key:string,value:Type):void;
getItem(key:string):ILocalStorgaeReturnValue<Type>;
removeItem(key:string ):void;
//clear();
}
export function LocalStorage<Type>(){
if( !window.localStorage)
throw new Error(`Browser does not support local storage`)
this.setItem = (key:string, val:Type ) =>{
if(!key || !val )
throw new Error(`key or value can not be null `)
const completeObj = {
storedValue: {
...val
},
date:new Date()
}
window.localStorage.setItem(key ,JSON.stringify(completeObj))
},
this.getItem = (key:string):ILocalStorgaeReturnValue<Type> =>{
if(!key )
throw new Error(`[key can not be null `)
const val= JSON.parse(window.localStorage.getItem(key))
if(!val || Object.keys(val).length<2)
return null
else
return <ILocalStorgaeReturnValue<Type>> {
...val
}
},
this.removeItem = (key:string) =>{
if(!key )
throw new Error(`Key is empty`)
window.localStorage.removeItem(key)
}
}

Ensure data types in Dexie.js fields

I have a Dexie.js database with the table "businessLayers" in my React application. I'd like to ensure de data types of the tuples inserted in that table. I thought the method Table.defineClass() would do that, but it does not. My db is the following:
import Dexie from 'dexie';
const db = new Dexie('MyDB');
db.version(1).stores({
businessLayers: '++id, layer'
});
const BusinessLayer = db.businessLayers.defineClass({
id: Number,
layer: String,
values: Object
});
export default db;
I'd like to make not possible to insert an invalid data type on each field. I haven't found any built-in method to do this. Do you know any? Thank you!
Table.defineClass() was an old feature in Dexie 1.x for code completion only - no enforcements. The method should have been deprecated. But the functionality you need can be implemented using a DBCore middleware or creating/updating hooks. DBCore middlware would be the most performant solution as it does not need to verify existing data.
Below is a dry coded full example. Please test and reply if it works. It should support String, Number, Boolean, Array, Object, Set, Map, ArrayBuffer, Uint8Array, etc... and even custom classes. If anyone wants to make a package of this code, please go ahead! I think it could be a nice addon to dexie:
import Dexie from 'dexie';
const db = new Dexie('MyDB');
db.version(1).stores({
businessLayers: '++id, layer'
});
// Use a DBCore middleware "enforceSchema" defined further down...
db.use(
enforceSchema({
businessLayers: {
id: Number,
layer: String,
values: Object
}
}
);
// This is the function that returns the middlware:
function enforceSchema(dbSchema) {
return {
stack: "dbcore",
name: "SchemaEnforcement",
create (downlevelDatabase) {
return {
...downlevelDatabase,
table (tableName) {
const downlevelTable = downlevelDatabase.table(tableName);
const tableSchema = dbSchema[tableName];
if (!tableSchema) return downlevelTable; // No schema for this table.
return {
...downlevelTable,
mutate: req => {
if (req.type === "add" || req.type === "put") {
for (obj of req.values) {
validateSchema(tableName, tableSchema, obj);
}
}
return downlevelTable.mutate(req);
}
}
}
};
}
};
}
function validateSchema(tableName, schema, obj) {
const invalidProp = Object.keys(schema).find(key =>
{
const value = obj[key];
const type = schema[key];
switch (type) {
// Handle numbers, strings and booleans specifically:
case Number: return typeof value !== "number";
case String: return typeof value !== "string";
case Boolean: return typeof value !== "boolean";
// All other types will be supported in the following
// single line:
default: return !(value instanceof type);
}
});
if (invalidProp) {
// Throw exception to abort the transaction and make the
// user get a rejected promise:
throw new TypeError(`Invalid type given for property ${invalidProp} in table ${tableName}. ${schema[invalidProp].name} expected.`);
}
}

Typescript named function expression

I'm starting to learning typescript applied to node JS backend.
For now i'm still using function and not class.
I used to write named function for each file like
const item= {
a:1,
b:2,
function1:()=>{
console.log(item.a)
},
function2:()=>{
console.log(item.b)
} }
then export it and use like item.function1. (i sometimes use it also as function1 with import as unstructured object)
Now using typescript i'm still using this approach but with types. The problem is that i can't assign a type to a because it's seen as value. I can't heither do :
const item= {
function1:()=>{
item.a = 3
console.log(item.a)
},
function2:()=>{
item.b = 4
console.log(item.b)
}}
because it's saying that property a or b does not exist in type item.
another thing i tried but that doesn't work is:
const item = {
function1:()=>{
item.a:number = 3
console.log(item.a)
},
function2:()=>{
item.b:number = 4
console.log(item.b)
} }
Anyone can help me? hoping that this named functions are not a bad habit for write code
When you declare a variable and don't specify its type, typescript will try to infer what its type is. When you assign an object, it will try to infer the type like this: look at what properties you wrote and consider that the object only has these properties with exactly the types that the object you wrote has. So in your last code example typescript will infer that item is of type
const item: {
function1: () => void
function2: () => void
} = {
// ...
}
You can see that it didn't add any other properties.
Next, if you don't declare a property when typing an object, typescript will think that this property doesn't exist and may not exist on the object. Consider this:
const obj: { foo: number } = {
foo: 6
}
obj.bar = 7 // Error
You didn't declare the bar property, so typescript doesn't allow to read it or assign something to it. This is why you cannot write item.a = 3 in your example: typescript didn't infer that item object has property a and it thinks that it must not exist
To solve this you just need to either assign all properties you will need when creating your object:
const item = {
a: 1,
b: 2,
function1: () => { /* ... */ },
function2: () => { /* ... */ },
}
Or type item manually
interface Item {
a?: number
b?: number
function1: () => void
function2: () => void
}
const item: Item = {
function1: () => {
item.a = 3
},
function2: () => {
item.b = 4
}
}
Note the question marks before the column inside the interface, this is to tell that these properties are optional. If you don't set these question marks, typescript will think these are obligatory, so it will emit an error if you create item and don't declare a and b properties
It is possible to denote item object as any.
To accomplish a desired consistency an interface could be defined as follows
interface ItemObject {
[key: string]: any
}
var item: ItemObject= {};
to make compact:
var item: {[k: string]: any} = {};
now item can accept any string as key and any type as value

Ecmascript function parameter with enum value

Is it possible to have an enum value of parameter in Ecmascript method?
For example for this case
export const testFunc = (param1) => {
};
For example, param can only take values of "val1","val2","val3"
export const testFunc = (param = {"val1","val2","val3"}) =>{
};
There no such thing as an enum in JS, but you could just check to see if the parameter is one of the allowed values:
export const testFunc = (param) =>{
if (!["val1","val2","val3"].includes(param)) {
throw new Error('Invalid param passed');
}
// rest of function
};
As Snow says, JavaScript doesn't have enums. TypeScript does (details), so if you want enums (and various other features), you might consider using TypeScript, which compiles to JavaScript.
If you don't want to go that route, you can always define an enum-like object:
const TheEnum = {
val1: 0,
val2: 1,
val3: 2,
0: "val1",
1: "val2",
2: "val3",
valid(value) {
return typeof param === "number" && !!TheEnum[param];
}
};
...and then validate the value you receive:
export const testFunc = (param) => {
if (!TheEnum.valid(param)) {
throw new Error("'param' must be a TheEnum value");
}
// ...
};
Note that that example "enum" has mappings both from symbolic names (val1, etc.0 to the values and from values to symbolic names. They do the in TypeScript, too; it's handy for when you want to show the name "val1" instead of the value 0 in messages, etc.

Throw error if config object doesn't contain all required properties

I have an ES6 class that require a config object.
If a property is missing, I'd like to throw an error.
The solution if found to keep the code short and not add a if(!object.propN)... for every property is to do :
class myClass {
constructor(config) {
if (!config) {
throw new Error("You must provide a config object");
}
this.prop1 = config.prop1;
this.prop2 = config.prop2;
this.prop3 = config.prop3;
this.prop4 = config.prop4;
for (const key in this) {
if (!this[key]) {
throw new Error(`Config object miss the property ${key}`);
}
}
}
}
Is it OK to do this in javascript ?
For configs, we usually use the feature Destructuring assignment to check whether the properties are in an object or not by using the sugar of ES6 (ES2015).
And furthermore, we can also set default values for the configs by this feature.
{prop1, prop2, prop3: path='', prop4='helloworld', ...others} = config
After destructuring assignment has been done, just need to do a check before what we are going to do with the specific config. i.e.
if (!prop1) {
throw new Error(`Config object miss the property ${prop1}`);
}
doSomething(prop1);
But if you still want to check all the configs at the beginning, you can do something like this,
class myClass {
constructor(config) {
if (!config) {
throw new Error("You must provide a config object");
}
// You can assign the values to a new object like "tmpConfig" or just assign them back to "config" for the case config.propX === undefined
({prop1: config.prop1=undefined, prop2: config.prop2=undefined, prop3: config.prop3=undefined, prop4: config.prop4=undefined} = config);
for (const key in config) {
if (!config[key]) {
throw new Error(`Config object miss the property ${key}`);
}
}
Object.assign(this, config);
}
}
Or only using Object.assign() by setting default values,
class myClass {
constructor(config) {
if (!config) {
throw new Error("You must provide a config object");
}
let requiredConfigs = {
prop1: undefined,
prop2: undefined,
prop3: undefined,
prop4: undefined,
}
let tmpConfig = Object.assign(requiredConfigs, config);
for (const key in tmpConfig) {
if (!tmpConfig[key]) {
throw new Error(`Config object miss the property ${key}`);
}
}
Object.assign(this, tmpConfig);
}
}
=> We use Destructuring assignment and Object.assign() a lot for doing setting configs things.
To avoid premature and excessive setting of all config properties into this object you should place the check for an empty property occurrence beforehand. The solution using Array.some function:
class myClass {
constructor(config) {
if (!config || typeof config !== "object") {
throw new Error("You must provide a config object!");
} else if (Object.keys(config).some((k) => !config[k])) { // returns 'true' if any empty property is found
throw new Error(`Config object has empty property(ies)!`);
}
this.prop1 = config.prop1;
this.prop2 = config.prop2;
this.prop3 = config.prop3;
this.prop4 = config.prop4;
}
}

Categories