Index signatures
It is possible to denote obj as any, but that defeats the whole purpose of using typescript. obj = {} implies obj is an Object. Marking it as any makes no sense. To accomplish the desired consistency an interface could be defined as follows, using an index signature
interface LooseObject {
[key: string]: any
}
var obj: LooseObject = {};
OR to make it compact:
var obj: {[k: string]: any} = {};
LooseObject can accept fields with any string as key and any type as value.
obj.prop = "value";
obj.prop2 = 88;
The real elegance of this solution is that you can include typesafe fields in the interface.
interface MyType {
typesafeProp1?: number,
requiredProp1: string,
[key: string]: any
}
var obj: MyType ;
obj = { requiredProp1: "foo"}; // valid
obj = {} // error. 'requiredProp1' is missing
obj.typesafeProp1 = "bar" // error. typesafeProp1 should be a number
obj.prop = "value";
obj.prop2 = 88;
Record<Keys,Type> utility type
Update (August 2020): @transang brought up the Record<Keys,Type> utility type in comments
Record<Keys,Type>is a Utility type in typescript. It is a much cleaner alternative for key-value pairs where property-names are not known. It's worth noting thatRecord<Keys,Type>is a named alias to{[k: Keys]: Type}whereKeysandTypeare generics. IMO, this makes it worth mentioning here
For comparison,
var obj: {[k: string]: any} = {};
becomes
var obj: Record<string,any> = {}
MyType can now be defined by extending Record type
interface MyType extends Record<string,any> {
typesafeProp1?: number,
requiredProp1: string,
}
While this answers the Original question, the answer here by @GreeneCreations might give another perspective on how to approach the problem.
Answer from Akash on Stack OverflowIndex signatures
It is possible to denote obj as any, but that defeats the whole purpose of using typescript. obj = {} implies obj is an Object. Marking it as any makes no sense. To accomplish the desired consistency an interface could be defined as follows, using an index signature
interface LooseObject {
[key: string]: any
}
var obj: LooseObject = {};
OR to make it compact:
var obj: {[k: string]: any} = {};
LooseObject can accept fields with any string as key and any type as value.
obj.prop = "value";
obj.prop2 = 88;
The real elegance of this solution is that you can include typesafe fields in the interface.
interface MyType {
typesafeProp1?: number,
requiredProp1: string,
[key: string]: any
}
var obj: MyType ;
obj = { requiredProp1: "foo"}; // valid
obj = {} // error. 'requiredProp1' is missing
obj.typesafeProp1 = "bar" // error. typesafeProp1 should be a number
obj.prop = "value";
obj.prop2 = 88;
Record<Keys,Type> utility type
Update (August 2020): @transang brought up the Record<Keys,Type> utility type in comments
Record<Keys,Type>is a Utility type in typescript. It is a much cleaner alternative for key-value pairs where property-names are not known. It's worth noting thatRecord<Keys,Type>is a named alias to{[k: Keys]: Type}whereKeysandTypeare generics. IMO, this makes it worth mentioning here
For comparison,
var obj: {[k: string]: any} = {};
becomes
var obj: Record<string,any> = {}
MyType can now be defined by extending Record type
interface MyType extends Record<string,any> {
typesafeProp1?: number,
requiredProp1: string,
}
While this answers the Original question, the answer here by @GreeneCreations might give another perspective on how to approach the problem.
This solution is useful when your object has Specific Type. Like when obtaining the object to other source.
let user: User = new User();
(user as any).otherProperty = 'hello';
//user did not lose its type here.
TypeScript- How to add a Key Value pair to each object in an Array?
How to add a property to an object that doesn't exist in its type?
Why TS is not allowing me to add properties to an object ?
How can I add a key/value pair to a JavaScript object? - Stack Overflow
Videos
What's wrong with your code is that idx will be the object not the index as you are using for...of. Use a simple regular for like:
Copyfor(let idx = 0; idx < data.length; idx++) {
data[idx].date = dates[idx];
}
Or use forEach to loop one of the arrays and use the index it provides to get the value from the other array:
Copydata.forEach((obj, i) => obj.date = dates[i]);
Copyconst result = data.map(({ name, age }, index) => ({ name, age, date: dates[index] }));
just map the array to the result.
EDIT: I just realized I could just deep clone the object and alter that one instead.
Heres a simplified example of what I'm trying to do:
const foo = (apiReponse: {A: number, B: number) => {
return apiReponse.D = 123
}Basically I am pass in an api response and using the type thats generated from codegen (apollo graphql). However I want to add a new field to it dynamically, however its throwing an error for the obvious reason.
How do I get around this or this just one big anti pattern?
You may say what's the point. but i'm trying to simplify a bigger situation here. we have a Person type and an object called originalPerson :
type Person = {
id: number;
name: string;
age: number;
email: string;
};
const originalPerson: Person = {
id: 1,
name: "John Doe",
age: 30,
email: "john.doe@example.com"
};Then we have unCompletePerson -I want to populate it with some of person's properties later-
const unCompletePerson: Partial<Person> = {};Now I want to add all the values that are strings to unCompletePerson
//get keys
const personKeys = Object.keys(originalPerson) as (keyof Person)[];
// Copy properties from `originalPerson` to `updatedPerson`
personKeys.forEach((key) => {
const value = originalPerson[key];
// We don't expect any values to be `undefined` in `originalPerson`
if (typeof value === 'string') unCompletePerson[key] = value
});TS says for unCompletePerson[key] : Type 'string' is not assignable to type 'undefined'. please explain like I'm five, why? what's the solution here ?
View code on playground
Thank you
There are two ways to add new properties to an object:
var obj = {
key1: value1,
key2: value2
};
Using dot notation:
obj.key3 = "value3";
Using square bracket notation:
obj["key3"] = "value3";
The first form is used when you know the name of the property. The second form is used when the name of the property is dynamically determined. Like in this example:
var getProperty = function (propertyName) {
return obj[propertyName];
};
getProperty("key1");
getProperty("key2");
getProperty("key3");
A real JavaScript array can be constructed using either:
The Array literal notation:
var arr = [];
The Array constructor notation:
var arr = new Array();
Year 2017 answer: Object.assign()
Object.assign(dest, src1, src2, ...) merges objects.
It overwrites dest with properties and values of (however many) source objects, then returns dest.
The
Object.assign()method is used to copy the values of all enumerable own properties from one or more source objects to a target object. It will return the target object.
Live example
var obj = {key1: "value1", key2: "value2"};
Object.assign(obj, {key3: "value3"});
document.body.innerHTML = JSON.stringify(obj);
Year 2018 answer: object spread operator {...}
obj = {...obj, ...pair, scalar};
From MDN:
It copies own enumerable properties from a provided object onto a new object.
Shallow-cloning (excluding prototype) or merging of objects is now possible using a shorter syntax than
Object.assign().Note that
Object.assign()triggers setters whereas spread syntax doesn’t.
Live example
It works in current Chrome and current Firefox. They say it doesn’t work in current Edge.
var obj = {key1: "value1", key2: "value2"};
var pair = {key3: "value3"};
var scalar = "value4"
obj = {...obj, ...pair, scalar};
document.body.innerHTML = JSON.stringify(obj);
Year 2019 answer
Object assignment operator +=:
obj += {key3: "value3"};
Oops... I got carried away. Smuggling information from the future is illegal. Duly obscured!
The code in your question is largely correct, here is a full working example:
const profile = {
"RouteID": "B76F77922EF83A4EE04024921F591A6F",
"Name": "3019998FALCON",
"rName": "KILGORE REMOVED"
}
profile["userID"] = "jzket";
// Works everywhere
console.log(profile["userID"]);
// Works, but violates the type information available here
console.log(profile.userID);
You'll notice that the type system will complain about the latter usage, because userID isn't part of the type inferred for profile.
You can either stick with the first example (profile['userID']) or provide a bit more type information:
interface Profile {
RouteID: string;
Name: string;
rName: string;
userID?: string;
}
const profile: Profile = {
"RouteID": "B76F77922EF83A4EE04024921F591A6F",
"Name": "3019998FALCON",
"rName": "KILGORE REMOVED"
}
profile["userID"] = "jzket";
// Works everywhere
console.log(profile["userID"]);
// Works, but violates the type information available here
console.log(profile.userID);
I totally agree with @Fenton. Maybe a little bit better approach is to add a new key / value pair with:
Object.assign like this:
const newProfile = Object.assign(profile, { userID: "jzket" });
Or with Spread Syntax:
const newProfile = ({...profile, userID: "jzket" });
JSFiddle example:
const profile = {
"ruteID":"B76F77922EF83A4EE04024921F591A6F",
"Name":"3019998FALCON",
"rName":"KILGORE REMOVED",
};
// With object assign
const exampleOne = Object.assign(profile, { userID: "jzket" });
// With spread syntax
const exampleTwo = ({...profile, userID: "jzket" })
console.log(exampleOne);
console.log(exampleTwo);
Useful links:
Spread Syntax
Object Assign
You are not modifying products in your array. You have two options:
- use
products.forEachand pass in a function that modifies a product. Return modified products. - use
products.mapand pass in a function that takes a product and creates new object with additional property, return the result of mapping.
I would argue that 2nd approach is more idiomatic in JS/TS.
const countTotalPrice = (products: Product[]): Product[] => {
return products.map((product) =>
({ // the parentheses are necessary in this position
// we want object literal, not code block
...product,
totalPrice: product.basePrice - product.discount
})
);
}
Playground link
try this
const countTotalPrice = (products: Product[]): Product[] => {
const calculateTotalPrice = (
p:Product
): void => {
p.totalPrice = p.basePrice -p. discount;
};
products.forEach((product) =>
calculateTotalPrice(
product
)
);
return products;
};