Destructuring assignment with rename and typing information - javascript

How do I destructure a variable into a new name while keeping typing information?
renderItem({item:'apple'})
// jsx:
function renderItem({item: region}) {
// region == 'apple'
return <div>{region}</div>;
}
The above will destructure an object with item and assign it to region.
How do I express typing information for this function signature?

Type the incoming item like so:
function renderItem({item: region}:{item:string}){}

Information about the typing of the feature is available in the TypeScript 2.1 documentation:
https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-1.html
Object rests are the dual of object spreads, in that they can extract any extra properties that don’t get picked up when destructuring an element:
When the Rest portion of the feature is used, it enhances object destructuring by enabling us to collect the rest of the properties into a new object comprised of them.
We can write the type annotation as we would for any other value. This is prefered as it can stop your function signatures getting verbose For example
interface IRenderItem {
item: String
}
function renderItem({ item: region }: IRenderItem): void {
console.log(item);
}

Related

weird Object Destructuring in Javascript { obj : { } }

I was looking at someone's code of a chrome extension where there is an API chrome.storage.local.get which basically takes two parameters: key and callback respectively.
suppose we have a object (named Highlights) stored in local storage. if I wanna access that object then I have to pass 'highlights' as first parameter (i.e. key) to the chrome.storage.local.get function.
code should look like :
chrome.storage.local.get('highlights', callback);
it works fine but the developer of the extension used a different approach, his code look like :
chrome.storage.local.get({highlights: {} }, callback);
Notice the key parameter { highlights : {} } surprisingly it gives the same results.
I just wanna know that what does { highlights: {} } means and why does it works.
some extra information :
highlights is an object of arrays.
snapshot of highlights object in console.log :
That's not destructuring. That's an object literal containing a property, highlights, set to an empty object.
According to the documentation (which was harder to find than I would have expected):
keys
string | string[] | object optional
A single key to get, list of keys to get, or a dictionary specifying default values (see description of the object). An empty list or object will return an empty result object. Pass in null to get the entire contents of storage.
(my emphasis)
So passing in that object is saying: "Give me the highlights value from storage, using {} as the default if there isn't any."

Passing a string as a function argument and using it as a dynamic key name in object.assign

I have a method in a service (my app is an Angular 5 app that is written in TypeScript) that takes two arguments, one is an event object and the other is a string which is the key of an object I store in the browser's web storage.
The method assigns a new object to the browser's web storage and combining it with a second object, here's my code
setPaginationObject(event: any, list: string) {
let sitePagination = JSON.parse(sessionStorage.getItem('sitePagination')) || {};
sitePagination = Object.assign(sitePagination, {list: event});
sessionStorage.setItem('sitePagination', JSON.stringify(sitePagination));
}
Here's how I would call in in a component:
public onPaginateChange(event: any): void {
this.paginationService.setPaginationObject(event, 'usersList');
}
My problem is that I need the argument to be assigned to the dynamic property used in the Object.assign code, as currently the declaration uses a the label "list" rather than the string passed as an argument. How can I pass an argument in my method and dynamically use it as a key name / label the Object.assign second argument?
I thought I could do something like this:
sitePagination = Object.assign(sitePagination, {`${list}`: event});
but that doesn't work, or I could change the argument signature to something like this so the object is formatted in the component and not the service
this.paginationService.setPaginationObject({'usersList': event});
and in the service
setPaginationObject(obj: any) {
let sitePagination = JSON.parse(sessionStorage.getItem('sitePagination')) || {};
sitePagination = Object.assign(sitePagination, obj);
sessionStorage.setItem('sitePagination', JSON.stringify(sitePagination));
}
The above seems fine but I now just want to know how I could dynamically set the key name in the Object.assign code.
Thanks in advance, sorry if my wording is bad, if it is I shall rework.
You can use a computed property inside your object literal:
let list: string = 'userList'
let obj = {[list]: 'Value'}; // will be { 'userList': 'Value'} at runtime

How to access an object by key in javascript or typescript whithin an unknown key

I have the following object and I wonder how can I access the 'value' key.
{ '-L7uAVxXmuLeBN1K-B0_': { timestamp: '18:35:18', value: 19.81 } }
I don not know the first key the '-L7uAVxXmuLeBN1K-B0_', but it always has the same structure. Is there a way to achieve this with javascript or typescript?
Here it is!
const value = yourObject[Object.keys(yourObject)[0]].value
With a bit of description:
You can access the object's keys by using Object.keys(obj) method which returns an array of its keys. Since your object has only one key, access it via Object.keys(obj)[0]. That's the key you don't know.
Now you have your key, access the inner object first via bracket notation:
obj[Object.keys(obj)[0]]
And then access your value!
Happy coding!

ES6 safe to use static class variable as key for Map?

In babel-preset-stage-0, we can declare static class variable as follow:
class Constants {
static COUNTRY = Object.freeze({
NAME: 'Germany',
DESCRIPTION: 'placeholder',
})
}
Is it safe to use Constants.COUNTRY as the key for a ES6 Map or Set?
eg.
const map = new Map();
map.add(Constants.COUNTRY, something);
Is it guaranteed that map.get(Constants.COUNTRY) will always return something?
Is the performance as good as using strings as key? Is it safe to use Constants.COUNTRY as eventKey (bootstrap component attribute) for NavItem too?
Is it also more appropriate to declare it as a variable instead of a class?
i.e.
const Constants = Object.freeze({
COUNTRY: Object.freeze({
NAME: 'Germany',
DESCRIPTION: 'placeholder',
})
})
Is it guaranteed that map.get(Constants.COUNTRY) will always return something?
For map.get(Constants.COUNTRY) to always return your original value, a couple things have to be true.
You have to make sure that Constants.COUNTRY can never be assigned a different value either because the .COUNTRY property was reassigned or because the Constants object was replaced with something else that had a different .COUNTRY property value.
You have to make sure that nobody can ever remove that key from the map object.
If you can assure these two things, then yes map.get(Constants.COUNTRY) will always return your desired value. But, if either of those are not necessarily true, then you are not assured of always getting your value from the map.
You can assure that Constants.COUNTRY cannot be changed by freezing the Constants object or by setting that property to be configurable to it cannot be removed or written to. To assure that the Constants object cannot be replaced, it would be best for it to be const as in your second code block.
I don't know of a way to assure that nobody can call map.delete(Constants.COUNTRY) except by keeping the map object private so foreign code cannot get to it.
If you had any reason to want to prevent enumeration of the keys in the map (to make it harder for someone to discover a key perhaps), then you could use a WeakMap instead of a Map.
Is the performance as good as using strings as key?
You'd have to test a specific Javascript implementation to be sure about performance. There is no required implementation reason that one or the other should be faster - it will just depend upon the internals of the implementation.
I created a jsPerf test case to compare string lookups to object lookups. Feedback is welcome on improving how this is tested/measured, but using the current scheme where I create 10,000 string keys and 10,000 object keys in a map and then compare accessing 1000 of each, I find varying results.
Chrome is ~20% slower to access the object keys.
Firefox is ~20% slower to access the string keys.
Edge is ~27% slower to access the string keys.
Is it also more appropriate to declare it as a variable instead of a class?
As discussed, your second const form has the advantage that Constants cannot be reassigned.
You can use WeakMap to COUNTRY as key, and something as value. A variable declared with const cannot be deleted. Along with you use of Object.freeze(), wm.get(COUNTRY) should always return something
const wm = new WeakMap;
const COUNTRY = Object.freeze({
NAME: 'Germany',
DESCRIPTION: 'placeholder',
});
wm.set(COUNTRY, "something");
// error when "use strict"
delete wm;
delete COUNTRY;
COUNTRY.NAME = 123;
console.log(
wm.get(COUNTRY)
);
console.log(
COUNTRY
);
console.log(
wm
);
If requirement is for a variable that cannot be deleted or changed you can use const see Is it possible to delete a variable declared using const? and JSON
"use strict";
// `Constants` cannot be changed or deleted
const Constants = `{
"NAME": "Germany",
"DESCRIPTION": "placeholder"
}`;
console.log(
JSON.parse(Constants)
);
// delete Constants;
/*
Error: {
"message": "Uncaught SyntaxError: Delete of an unqualified identifier in strict mode."
}
*/

Symbol in Javascript

I saw following code in a project. can anyone explain what is going on here? What will be value of Attributes? What is happening this[Attributes] = attrs line?
const Attributes = Symbol('User#attrs');
class User {
constructor (attrs) {
this[Attributes] = attrs;
}
}
Symbol creates an un-collidable key for any object:
const first = Symbol('debug-name');
const second = Symbol('debug-name');
first !== second // true;
const anObj = {};
anObj[first] = 123;
anObj[second] = 456;
console.log(anObj) // {Symbol('debug-name'): 123, Symbol('debug-name'): 456}
Note that even though the first and second variables have the same debugging string they create different keys in anObj. Anyone who has access to first can add that key to any object and it will not collide with any other key in that object.
This can be used instead of magic strings to manage protocols:
// ES5
someObject.MY_LIB_attributes = [1, 2, 3];
// Only safe if no other library uses the key
// "MY_LIB_attributes"
// ES2015+
export const Attributes = Symbol('myLib#attributes');
import { Attributes } from 'my-lib';
someObj[Attributes] = [1, 2, 3];
// Safe as long as no other library uses
// *this* Symbol instance for some other purpose.
Edit
Since you've now clarified the question to be only about the line of code this[Attributes] = attrs, see the second part of my answer for discussion of that.
Original Answer
This is a couple of the new ES6 Javascript features.
const Attributes = Symbol('User#attrs'); creates a new Symbol object. The Symbol function and object is described here. It creates a unique identifier object that can then be used for many other uses, one of which is as a property name. There are many other references on the new Symbol feature so I won't repeat all of that here.
The class definition is the ES6 method for declaring prototyped classes. Again, there are many other references on this new syntax so there is no point in repeating all that here. There's an example below of what the equivalent ES5 code is.
This line this[Attributes] = attrs; uses the Symbol generated above to set a property on the newly created object.
The class definition is equivalent to the regular constructor
declaration like this:
function User(attrs) {
this[Attributes] = attrs;
}
Discussion of this[Attributes] = attrs
Attributes is a symbol which can be used as a property name on an object. It's a way of generating a unique key that can be used as a property name. So, this[Attributes] = attrs is setting a property on the newly constructed object and it is using the Attributes symbol as the property name. This Attributes symbol is a unique value that will not match any known string (in fact it won't even match other Symbol objects) so it's a way of making a unique property name.
It is unclear why the code does this:
this[Attributes] = attrs;
instead of just something like this:
this.attrs = attrs;
We would have to see a bit more context for how that is being used and why a plain string property could not also be used in place of the Symbol as you haven't provided enough context for us to know.
One possible use is for privacy. If Attributes is not public, then this is a way of creating a property on the object that the outside world doesn't know how to access because you have to have the current value of Attributes in order to access that properly. As you've shown the code with User and Attributes in the same scope that does not seem like it is private, but perhaps only User is exported to a public scope.
Another possible use is for uniqueness. If the User object may have lots of other properties added to it by other code, then Attributes creates a unique property name that cannot collide with other property names. This seems less likely in this case, but it is one possible use of Symbols.

Categories

Resources