I am new on Typescript and I don't know how to solve a trivial issue I guess.
I've declared a new class, and then I want to assign the values I got from the apiCall. But the problem I've get is:
Cannot read property 'list_ids' of undefined
On my class, I inicializated the object, and if I want to declare it empty, it appears a mistake.
How can I solve this? If I could declare it empty from the beggining and then assign it, it would be great I guess!
Here I post the code of my class:
export class MyClass {
name: string
op: string
field_id: string
comparsion_values: {
values: [
string
],
list_ids: [number]
field_ids: [
string
],
velocities: [
string
],
prices: [
{
amount: 0
currency: string
}
]
}
}
I Inizializate it as: myObjectClass= new myClass;
And the problem appear when I try to do a push in some of my arrays.
Thank you a lot.
Problem 1:
Based on the naming, I assume you intended list_ids, field_ids to be arrays, but currently they are typed as tuples.
Problem 2:
You are not initializing your comparsion_values property. You are simply defining the type for the property.
You can define the type for comparsion_values in an interface (there is nothing wrong with defining it inline, but it looks messy) and initialize it with empty arrays in the constructor like below:
interface ComparisonValues {
values: Array<string>, // Or string[] would work too
list_ids: Array<number>
field_ids: Array<string>,
velocities: Array<string>,
prices: Array<{
amount: number
currency: string
}>
}
export class MyClass {
name: string
op: string
field_id: string
comparsion_values: ComparisonValues
constructor() {
this.comparsion_values = {
values: [],
list_ids: [],
field_ids: [],
velocities: [],
prices: []
}
}
}
Related
I have a class with a property on it, which can be a number of classes based on a property. #Type obviously is perfect for this, but the issue is, the discriminator does not exist on the object, it exists on the parent.
Consider the following:
class Parent {
type: 'a' | 'b'
#Type(() => ?, {
discriminator: {
property: 'type',
subTypes: [
{ value: TypeA, name: 'a' },
{ value: TypeB, name: 'b' },
]
}
}
data: TypeA | TypeB
}
Naturally I can't do this. I tried a custom decorator which does something like:
const TypeOnParent: () => PropertyDecorator = () => {
const __class__ = class {}
const prop = '__type'
return (target, key) => {
Transform(({ value, obj }) => {
value[prop] = obj.type
return value
})(target, key)
Type(() => __class__, {
keepDiscriminatorProperty: true,
discriminator: {
property: prop,
subTypes: [
{ name: 'a', value: TypeA },
{ name: 'b', value: TypeB },
],
},
})(target, key)
}
}
class Parent {
type: 'a' | 'b'
#TypeOnParent('type')
data: TypeA | TypeB
}
The goal here is to pass the parent prop onto the child, so that Type discriminator can do its job. However, the discriminator prop that I pass onto the child 'data' prop doesn't seem to work. It just defaults to using the class instance. I've tried changing the order of the decorators.
The result is an identical object no matter what, but if I pass the value in manually via payload, it works fine. If I use the transform, it never works.
Am I missing something? Does class-transform ALWAYS run type before any other decorators? Or is there a better way to achieve this?
I am using nestjs global validation pipe if that helps.
I have solved this not by using discriminator, but the type function.
Type will always run first, before Transform, so Transform is not an option.
However, it passes an object into the function, so we can simply use that:
#Type((opts) => opts.object.type === 'a' ? TypeA : TypeB)
data: TypeA | TypeB
here I have a variable named body which will receive some data later and for now it is assigned as null:
const body: {
"name": string,
"photo": {
"fileName": string,
"file": NodeJS.ReadableStream,
"encoding": string,
"mimetype": string,
"sizeInBytes": number,
"publicUrl": string
},
"token": string
} = null;
but later when I receive data and try to put data like this:
body[someVariable] = someVariable;
it transpiles with no error but when I run js file it gives me error like this:
Uncaught TypeError: Cannot set property 'fieldName1' of undefined
I searched on internet and found that an object must be initialized as with {} empty object to add properties further in it, but if I do something like that e.g: body = {} typescript error ays values are missing, I am not in position to make those values optional
There are two sides to your question.
First of all, you can't assign properties of a null or undefined object. In other
words, if your variable is initialized to null, you can't access one of its properties and assign a value to it. That's a JavaScript error.
Then comes the TypeScript error. It looks like you want to declare optional properties on your object. You can do it with the ? operator:
const body: {
name?: string,
photo?: {
fileName?: string,
file?: NodeJS.ReadableStream,
encoding?: string,
mimetype?: string,
sizeInBytes?: number,
publicUrl?: string
},
token?: string
} = null;
or with the Partial generic type:
interface BodyType {
name: string,
photo: {
fileName: string,
file: NodeJS.ReadableStream,
encoding: string,
mimetype: string,
sizeInBytes: number,
publicUrl: string
},
token: string
}
const body: Partial<BodyType> = {};
For more information about the Partial generic, takle a look here.
This way, you'll be able to specify the type of your object, without having to fill in every declared property at initialization. Ideally you wouldn't use the Partial except in specific occasions, and instead declare what properties are optional and what other are mandatory on an interface.
In any case, it depends on what your types mean. Does it make sense to have the BodyType interface, with all mandatory props? Then go for the Partial approach. Do you know in advance which properties can be undefined and which one can't? Then use the ? operator.
I am having a problem searching for a key of a nested object.
I have search criteria object that may or may not have certain fields I'd like to search on.
The way I'm solving this is to use conditional statements to append to a "match criteria" object that gets passed to the aggregate $match operator. it works well until I need to match to something inside a nested object.
Here is a sample document structure
{
name: string,
dates: {
actived: Date,
suspended: Date
},
address : [{
street: string,
city: string,
state: string,
zip: string
}]
};
My criteria object is populated thru a UI and passed a JSON that looks similar to this:
{
"name": "",
"state": ""
}
And although I can explicitly use "dates.suspended" without issue -
when I try to append address.state to my search match criteria - I get an error.
module.exports.search = function( criteria, callback )
let matchCriteria = {
"name": criteria.name,
"dates.suspended": null
};
if ( criteria.state !== '' ) {
// *** PROBLEM HAPPENS HERE *** //
matchCriteria.address.state = criteria.state;
}
User.aggregate([
{ "$match": matchCriteria },
{ "$addFields": {...} },
{ "$project": {...} }
], callback );
}
I get the error:
TypeError: Cannot set property 'state' of undefined
I understand that I'm specifying 'address.state' when 'address' doesn't exist yet - but I am unclear what my syntax would be surely it woulnd't be matchCriteria['address.state'] or "matchCriteria.address.state"
Is there a better way to do conditional filtering?
For search in Nested Object, You have to use unwind
A query that help you :
//For testing declare criteria as const
let criteria = {name : 'name', 'state' : 'state'};
let addressMatch = {};
let matchCriteria = {
"name": criteria.name,
"dates.suspended": null
};
if ( criteria.state) {
addressMatch = { 'address.state' : criteria.state };
}
db.getCollection('user').aggregate([{
$match :matchCriteria,
},{$unwind:'$address'},
{$match : addressMatch}
])
Firstly check for address, and then access the property as shown:
if(matchCriteria['address']) {
matchCriteria['address']['state'] = criteria['state'];
}
else {
//otherwise
}
This should fix it:
matchCriteria['address.state'] = criteria.state;
Given the schema:
{
_id: ObjectID,
city:
{ units:
{ abc: {},
def: { tuid : String },
...
xxx: { tuid : String }
}
}
I would like to return, for a particular _id, all the properties of units who's subproperty tuid is, for example, 123.
I have searched for information about this but array operations keep popping up instead of what I need.
Thank you.
We can generate an object-type json by groovy's json builder:
def builder = new groovy.json.JsonBuilder()
def root = builder.people {
person {
firstName 'Guillame'
lastName 'Laforge'
// Named arguments are valid values for objects too
address(
city: 'Paris',
country: 'France',
zip: 12345,
)
married true
// a list of values
conferences 'JavaOne', 'Gr8conf'
}
}
def jsonStr = builder.toString()
I like this type of syntax, but how to build an array-type json?
E.g.
[
{"code": "111", "value":"222"},
{"code": "222", "value":"444"}
]
I found some documents which say we should use JsonBuilder() constructor:
def mydata = [ ["code": "111", "value":"222"],["code": "222", "value":"444"] ]
def builder = new groovy.json.JsonBuilder(mydata)
def jsonStr = builder.toString()
But I preferred the first syntax. Is it able to use it generate array-type json?
The syntax you propose doesn't look possible, as I don't believe it's valid groovy. A closure such as {"blah":"foo"} doesn't makes sense to groovy, and you're going to be constrained by syntactical limitations. I think the best you're going to be able to do is something within the following:
def root = builder.call (
[
{
code "111"
value "222"
},
{code "222"; value "444"}, //note these are statements within a closure, so ';' separates instead of ',', and no ':' used
[code: "333", value:"555"], //map also allowed
[1,5,7] //as are nested lists
]
)
it is also possible to create list of closures and pass it to builder
import groovy.json.*
dataList = [
[a:3, b:4],
[a:43, b:3, c:32]
]
builder = new JsonBuilder()
builder {
items dataList.collect {data ->
return {
my_new_key ''
data.each {key, value ->
"$key" value
}
}
}
}
println builder.toPrettyString()
I like conversion in the end more than builder,
def json = [
profile: [
_id: profile._id,
fullName: profile.fullName,
picture: profile.picture
]
,title: title
,details: details
,tags: ["tag1","tag2"]
,internalTags: ["test"]
,taggedProfiles: []
] as JSON