I have an object in NodeJS (a socket to be accurate).
When I print it, I see that one of the entries is this:
[Symbol(asyncId)]: 2781 // the numeric value changes
How can I obtain the value of such key?
I've tried socket['[Symbol(asyncId)]'] but got undefined.
The expression socket.[Symbol(asyncId)] would obviously not work.
You will not be able to access it directly by key, unless you have a reference to the actual: Symbol('asyncId'), because every Symbol is unique
The Symbol() function returns a value of type symbol, has static
properties that expose several members of built-in objects, has static
methods that expose the global symbol registry, and resembles a
built-in object class but is incomplete as a constructor because it
does not support the syntax "new Symbol()".
What you can do is loop through the object's own property keys, using Reflect.ownKeys, which will include normal properties & symbols, and then obtain that reference.
You can also use: Object.getOwnPropertySymbols()
function getObject() {
// You don't have access to this symbol, outside of this scope.
const symbol = Symbol('asyncId');
return {
foo: 'bar',
[symbol]: 42
};
}
const obj = getObject();
console.log(obj);
console.log(obj[Symbol('asyncId')]); // undefined
// or Object.getOwnPropertySymbols(obj)
const symbolKey = Reflect.ownKeys(obj)
.find(key => key.toString() === 'Symbol(asyncId)')
console.log(obj[symbolKey]); // 42
NOTE: The object can have multiple keys where key.toString() === 'Symbol(asyncId)', this won't be usual, but be aware, so you may want to use other function other than .find if that's the case.
NOTE II:
You should not change the value of that property, since it's supposed to be for internal access only, even if the property is not read only.
function getObject() {
// You don't have access to this symbol, outside of this scope.
const symbol = Symbol('asyncId');
const symbol2 = Symbol('asyncId');
return {
foo: 'bar',
[symbol]: 'the value I don\'t want',
[symbol2]: 'the value I want'
};
}
const obj = getObject();
const symbolKey = Reflect.ownKeys(obj)
.find(key => key.toString() === 'Symbol(asyncId)')
console.log(obj[symbolKey]); // the value I don't want
console.log('=== All values ===');
Reflect.ownKeys(obj)
.forEach(key => console.log(obj[key]));
Use Object.getOwnPropertySymbols(obj) and iterate over it
You need to store the Symbol in advance and use it as accessor for the object.
Every symbol value returned from Symbol() is unique. A symbol value may be used as an identifier for object properties; this is the data type's only purpose. Some further explanation about purpose and usage can be found in the glossary entry for Symbol.
var asyncId = 42,
symbol = Symbol(asyncId),
object = { [symbol]: 2781 };
console.log(object[symbol]);
console.log(symbol.toString());
Related
I am debugging an app, there is an existing redux reducer which sets some data of store object. Now when i dispatch action for this reducer before the relevant object is initialised it still works and create an empty object. This works on our deployment server and do crash on my local machine with correct error that "map is undefined on null". Why is it creating an empty object and not crashing on deployment server and if it is creating an object why is it not assigning the data we pass to it. My reducer is
case ACTIONS.SET_LOCAL_WEIGHTS: {
const { weight } = action;
const drafts = fromJS(state.getIn(['draftData', 'rows']));
const setWeight = drafts.map((row: any) => {
row.data.weight = weight[row.id].weight;
return row;
});
return state
.setIn(['draftData', 'rows'], setWeight)
.setIn(['draftData', 'total'], setWeight.length);
}
It creates: draftData: {} when rows and total is also provided. I have tried it on node 15 and 12 for checking any anomaly on map function.
I get error Cannot read property 'map' of undefined on your code if the initial state doesn't have a property state.draftData.rows. I don't see anywhere where you would be creating an empty object.
The immutable.js fromJS method will create a List if called with an array from state.draftData.rows. But if it is called with undefined then it returns undefined instead of a collection with a .map() method.
I also don't think that you need to be calling fromJS if the rows object is never converted toJS, but it might depend on your initial state.
This code should work. It uses the existing List from state if it exists, or creates an empty List otherwise.
const drafts = state.getIn(["draftData", "rows"]) ?? fromJS([]);
The assignment in row.data.weight = weight[row.id].weight seems like a mutation of state.
I tried to rewrite this, but it seems strange to me that your code doesn't do anything with the weights in the payload unless their index/key matches one that's already in the state.
import { fromJS, List, Map } from "immutable";
interface Row {
data: {
weight: number;
};
id: number;
}
const reducer = (state = Map(), action) => {
switch (action.type) {
case ACTIONS.SET_LOCAL_WEIGHTS: {
const { weight } = action;
const drafts: List<Row> =
state.getIn(["draftData", "rows"]) ?? fromJS([]);
const setWeight = drafts.reduce(
(next, row, index) =>
next.setIn([index, "data", "weight"], weight[row.id]?.weight),
drafts
);
return state
.setIn(["draftData", "rows"], setWeight)
.setIn(["draftData", "total"], setWeight.size);
}
default:
return state;
}
};
I’m getting a number of warnings all relating to the use of ‘any’ as a return type for functions in my Typscript code. I am trying to write a node.js backend with Cloud Functions for Firebase, to manage Google Play Billing purchases and subscriptions.
I am following the examples given in the Classy Taxi Server example here:
https://github.com/android/play-billing-samples/tree/main/ClassyTaxiServer
For example, the following:
function purchaseToFirestoreObject(purchase: Purchase, skuType: SkuType): any {
const fObj: any = {};
Object.assign(fObj, purchase);
fObj.formOfPayment = GOOGLE_PLAY_FORM_OF_PAYMENT;
fObj.skuType = skuType;
return fObj;
}
Gives the warning
Unexpected any. Specify a different type. #typescript-eslint/no-explicit-any)
I have tried to change ‘any’ to ‘unknown’, but then I get an error
Property 'formOfPayment' does not exist on type 'unknown'.ts(2339)
and
Property 'skuType' does not exist on type 'unknown'.ts(2339)
In another function
export function mergePurchaseWithFirestorePurchaseRecord(purchase: Purchase, firestoreObject: any) {
// Copy all keys that exist in Firestore but not in Purchase object, to the Purchase object (ex. userID)
Object.keys(firestoreObject).map(key => {
// Skip the internal key-value pairs assigned by convertToFirestorePurchaseRecord()
if ((purchase[key] === undefined) && (FIRESTORE_OBJECT_INTERNAL_KEYS.indexOf(key) === -1)) {
purchase[key] = firestoreObject[key];
}
});
}
I get the following warnings
Missing return type on function. #typescript-eslint/explicit-module-boundary-types
Argument 'firestoreObject' should be typed with a non-any type. #typescript-eslint/explicit-module-boundary-types
Unexpected any. Specify a different type. #typescript-eslint/no-explicit-any
In this function, if I change ‘any’ to ‘unknown’, I still get a warning for
Missing return type on function.
In another example I get an error for the use of ‘any’ in this constructor:
export default class PurchaseManager {
constructor(private purchasesDbRef: CollectionReference, private playDeveloperApiClient: any) { }
And again the warning is
Argument 'playDeveloperApiClient' should be typed with a non-any type. #typescript-eslint/explicit-module-boundary-types
In this case, if I follow the suggestion to use ‘unknown’ instead of ‘any’, then I get an error for ‘purchases’ in the following function:
const apiResponse = await new Promise((resolve, reject) => {
this.playDeveloperApiClient.purchases.products.get({
packageName: packageName,
productId: sku,
token: purchaseToken,
}, (err, result) => {
if (err) {
reject(this.convertPlayAPIErrorToLibraryError(err));
} else {
resolve(result.data);
}
})
});
The error created by changing ‘any’ to ‘unknown’ in the constructor is:
Property 'purchases' does not exist on type 'unknown'.ts(2339)
If I understand correctly, I could prevent all of these (and other similar) warnings without creating errors, by disabling explicit-module-boundary-types, and/or no-explicit-any for the entire file, but I am not sure if this is bad practice?
Is there another (better) way to specify the return types to avoid using ‘any’?
Or is it fine to just go ahead and disable explicit-module-boundary-types or no-explicit-any?
If you do not want to use any, you will have to declare an interface for your type and set that as the return type of the function.
You can do something like:
interface MyType {
formOfPayment: string;
skyType: SkyType;
foo: //<any other pre-existing type>;
}
Then you can use this as the return type of any other function that requires you to return an object with above properties. e.g.
function purchaseToFirestoreObject(purchase: Purchase, skuType: SkuType): MyType {
const fObj: any = {};
Object.assign(fObj, purchase);
fObj.formOfPayment = GOOGLE_PLAY_FORM_OF_PAYMENT;
fObj.skuType = skuType;
return fObj;
}
I'm working on a function where I need to be able to input a string which is a key in a JSON object then I need to be able to take the actual object and tack on the string to get the correct value from the JSON
function contact(contact_method) {
let method = array[place].settings.contact_method; // Example for contact_method is 'first_contact_method'
console.log(method)
}
The idea is I have 3 different contact methods and I'd like to be able to use the same function for all 3. I know the code above is barely a function but I think it shows what I want to be able to do.
I could not find anything on MDN or SO about this. I had tried using ES6 and string with `` but that did not work it just returned [object Object].first_contact_method
You can access keys of objects with a variable by using [].
For instance:
const obj = { a: 4, b: 5, c: () => { /* do something*/}, d() { /* do something*/ } }
const keyA = 'a'
const keyC = 'c'
const valueA = obj[keyA] // valueA === 4
const methodC = obj[keyC]
// Call method c
methodC()
// or short
obj[keyC]()
// and even for "real" methods
obj['d']()
It's difficult to explain the case by words, let me give an example:
var myObj = {
'name': 'Umut',
'age' : 34
};
var prop = 'name';
var value = 'Onur';
myObj[name] = value; // This does not work
eval('myObj.' + name) = value; //Bad coding ;)
How can I set a variable property with variable value in a JavaScript object?
myObj[prop] = value;
That should work. You mixed up the name of the variable and its value. But indexing an object with strings to get at its properties works fine in JavaScript.
myObj.name=value
or
myObj['name']=value (Quotes are required)
Both of these are interchangeable.
Edit: I'm guessing you meant myObj[prop] = value, instead of myObj[name] = value. Second syntax works fine: http://jsfiddle.net/waitinforatrain/dNjvb/1/
You can get the property the same way as you set it.
foo = {
bar: "value"
}
You set the value
foo["bar"] = "baz";
To get the value
foo["bar"]
will return "baz".
You could also create something that would be similar to a value object (vo);
SomeModelClassNameVO.js;
function SomeModelClassNameVO(name,id) {
this.name = name;
this.id = id;
}
Than you can just do;
var someModelClassNameVO = new someModelClassNameVO('name',1);
console.log(someModelClassNameVO.name);
simple as this
myObj.name = value;
When you create an object myObj as you have, think of it more like a dictionary. In this case, it has two keys, name, and age.
You can access these dictionaries in two ways:
Like an array (e.g. myObj[name]); or
Like a property (e.g. myObj.name); do note that some properties are reserved, so the first method is preferred.
You should be able to access it as a property without any problems. However, to access it as an array, you'll need to treat the key like a string.
myObj["name"]
Otherwise, javascript will assume that name is a variable, and since you haven't created a variable called name, it won't be able to access the key you're expecting.
You could do the following:
var currentObj = {
name: 'Umut',
age : 34
};
var newValues = {
name: 'Onur',
}
Option 1:
currentObj = Object.assign(currentObj, newValues);
Option 2:
currentObj = {...currentObj, ...newValues};
Option 3:
Object.keys(newValues).forEach(key => {
currentObj[key] = newValues[key];
});
I am aware of how to create getters and setters for properties whose names one already knows, by doing something like this:
// A trivial example:
function MyObject(val){
this.count = 0;
this.value = val;
}
MyObject.prototype = {
get value(){
return this.count < 2 ? "Go away" : this._value;
},
set value(val){
this._value = val + (++this.count);
}
};
var a = new MyObject('foo');
alert(a.value); // --> "Go away"
a.value = 'bar';
alert(a.value); // --> "bar2"
Now, my question is, is it possible to define sort of catch-all getters and setters like these? I.e., create getters and setters for any property name which isn't already defined.
The concept is possible in PHP using the __get() and __set() magic methods (see the PHP documentation for information on these), so I'm really asking is there a JavaScript equivalent to these?
Needless to say, I'd ideally like a solution that is cross-browser compatible.
This changed as of the ES2015 (aka "ES6") specification: JavaScript now has proxies. Proxies let you create objects that are true proxies for (facades on) other objects. Here's a simple example that turns any property values that are strings to all caps on retrieval, and returns "missing" instead of undefined for a property that doesn't exist:
"use strict";
if (typeof Proxy == "undefined") {
throw new Error("This browser doesn't support Proxy");
}
let original = {
example: "value",
};
let proxy = new Proxy(original, {
get(target, name, receiver) {
if (Reflect.has(target, name)) {
let rv = Reflect.get(target, name, receiver);
if (typeof rv === "string") {
rv = rv.toUpperCase();
}
return rv;
}
return "missing";
}
});
console.log(`original.example = ${original.example}`); // "original.example = value"
console.log(`proxy.example = ${proxy.example}`); // "proxy.example = VALUE"
console.log(`proxy.unknown = ${proxy.unknown}`); // "proxy.unknown = missing"
original.example = "updated";
console.log(`original.example = ${original.example}`); // "original.example = updated"
console.log(`proxy.example = ${proxy.example}`); // "proxy.example = UPDATED"
Operations you don't override have their default behavior. In the above, all we override is get, but there's a whole list of operations you can hook into.
In the get handler function's arguments list:
target is the object being proxied (original, in our case).
name is (of course) the name of the property being retrieved, which is usually a string but could also be a Symbol.
receiver is the object that should be used as this in the getter function if the property is an accessor rather than a data property. In the normal case this is the proxy or something that inherits from it, but it can be anything since the trap may be triggered by Reflect.get.
This lets you create an object with the catch-all getter and setter feature you want:
"use strict";
if (typeof Proxy == "undefined") {
throw new Error("This browser doesn't support Proxy");
}
let obj = new Proxy({}, {
get(target, name, receiver) {
if (!Reflect.has(target, name)) {
console.log("Getting non-existent property '" + name + "'");
return undefined;
}
return Reflect.get(target, name, receiver);
},
set(target, name, value, receiver) {
if (!Reflect.has(target, name)) {
console.log(`Setting non-existent property '${name}', initial value: ${value}`);
}
return Reflect.set(target, name, value, receiver);
}
});
console.log(`[before] obj.example = ${obj.example}`);
obj.example = "value";
console.log(`[after] obj.example = ${obj.example}`);
The output of the above is:
Getting non-existent property 'example'
[before] obj.example = undefined
Setting non-existent property 'example', initial value: value
[after] obj.example = value
Note how we get the "non-existent" message when we try to retrieve example when it doesn't yet exist, and again when we create it, but not after that.
Answer from 2011 (obsoleted by the above, still relevant to environments limited to ES5 features like Internet Explorer):
No, JavaScript doesn't have a catch-all property feature. The accessor syntax you're using is covered in Section 11.1.5 of the spec, and doesn't offer any wildcard or something like that.
You could, of course, implement a function to do it, but I'm guessing you probably don't want to use f = obj.prop("example"); rather than f = obj.example; and obj.prop("example", value); rather than obj.example = value; (which would be necessary for the function to handle unknown properties).
FWIW, the getter function (I didn't bother with setter logic) would look something like this:
MyObject.prototype.prop = function(propName) {
if (propName in this) {
// This object or its prototype already has this property,
// return the existing value.
return this[propName];
}
// ...Catch-all, deal with undefined property here...
};
But again, I can't imagine you'd really want to do that, because of how it changes how you use the object.
Preface:
T.J. Crowder's answer mentions a Proxy, which will be needed for a catch-all getter/setter for properties which don't exist, as the OP was asking for. Depending on what behavior is actually wanted with dynamic getters/setters, a Proxy may not actually be necessary though; or, potentially, you may want to use a combination of a Proxy with what I'll show you below.
(P.S. I have experimented with Proxy thoroughly in Firefox on Linux recently and have found it to be very capable, but also somewhat confusing/difficult to work with and get right. More importantly, I have also found it to be quite slow (at least in relation to how optimized JavaScript tends to be nowadays) - I'm talking in the realm of deca-multiples slower.)
To implement dynamically created getters and setters specifically, you can use Object.defineProperty() or Object.defineProperties(). This is also quite fast.
The gist is that you can define a getter and/or setter on an object like so:
let obj = {};
let val = 0;
Object.defineProperty(obj, 'prop', { //<- This object is called a "property descriptor".
//Alternatively, use: `get() {}`
get: function() {
return val;
},
//Alternatively, use: `set(newValue) {}`
set: function(newValue) {
val = newValue;
}
});
//Calls the getter function.
console.log(obj.prop);
let copy = obj.prop;
//Etc.
//Calls the setter function.
obj.prop = 10;
++obj.prop;
//Etc.
Several things to note here:
You cannot use the value property in the property descriptor (not shown above) simultaneously with get and/or set; from the docs:
Property descriptors present in objects come in two main flavors: data descriptors and accessor descriptors. A data descriptor is a property that has a value, which may or may not be writable. An accessor descriptor is a property described by a getter-setter pair of functions. A descriptor must be one of these two flavors; it cannot be both.
Thus, you'll note that I created a val property outside of the Object.defineProperty() call/property descriptor. This is standard behavior.
As per the error here, don't set writable to true in the property descriptor if you use get or set.
You might want to consider setting configurable and enumerable, however, depending on what you're after; from the docs:
configurable
true if and only if the type of this property descriptor may be changed and if the property may be deleted from the corresponding object.
Defaults to false.
enumerable
true if and only if this property shows up during enumeration of the properties on the corresponding object.
Defaults to false.
On this note, these may also be of interest:
Object.getOwnPropertyNames(obj): gets all properties of an object, even non-enumerable ones (AFAIK this is the only way to do so!).
Object.getOwnPropertyDescriptor(obj, prop): gets the property descriptor of an object, the object that was passed to Object.defineProperty() above.
obj.propertyIsEnumerable(prop);: for an individual property on a specific object instance, call this function on the object instance to determine whether the specific property is enumerable or not.
The following could be an original approach to this problem:
var obj = {
emptyValue: null,
get: function(prop){
if(typeof this[prop] == "undefined")
return this.emptyValue;
else
return this[prop];
},
set: function(prop,value){
this[prop] = value;
}
}
In order to use it the properties should be passed as strings.
So here is an example of how it works:
//To set a property
obj.set('myProperty','myValue');
//To get a property
var myVar = obj.get('myProperty');
Edit:
An improved, more object-oriented approach based on what I proposed is the following:
function MyObject() {
var emptyValue = null;
var obj = {};
this.get = function(prop){
return (typeof obj[prop] == "undefined") ? emptyValue : obj[prop];
};
this.set = function(prop,value){
obj[prop] = value;
};
}
var newObj = new MyObject();
newObj.set('myProperty','MyValue');
alert(newObj.get('myProperty'));
You can see it working here.
I was looking for something and I figured out on my own.
/*
This function takes an object and converts to a proxy object.
It also takes care of proxying nested objectsa and array.
*/
let getProxy = (original) => {
return new Proxy(original, {
get(target, name, receiver) {
let rv = Reflect.get(target, name, receiver);
return rv;
},
set(target, name, value, receiver) {
// Proxies new objects
if(typeof value === "object"){
value = getProxy(value);
}
return Reflect.set(target, name, value, receiver);
}
})
}
let first = {};
let proxy = getProxy(first);
/*
Here are the tests
*/
proxy.name={} // object
proxy.name.first={} // nested object
proxy.name.first.names=[] // nested array
proxy.name.first.names[0]={first:"vetri"} // nested array with an object
/*
Here are the serialised values
*/
console.log(JSON.stringify(first)) // {"name":{"first":{"names":[{"first":"vetri"}]}}}
console.log(JSON.stringify(proxy)) // {"name":{"first":{"names":[{"first":"vetri"}]}}}
var x={}
var propName = 'value'
var get = Function("return this['" + propName + "']")
var set = Function("newValue", "this['" + propName + "'] = newValue")
var handler = { 'get': get, 'set': set, enumerable: true, configurable: true }
Object.defineProperty(x, propName, handler)
this works for me