Im trying to set bob
via variable key;
interface Person {
name:string,
age:number
}
const bob:Person = {
name:'bob',
age:12
}
function setSome<T extends Person>(payload:Partial<T>){
Object.keys(payload).forEach((key)=>{
bob[key] = payload[key] // ts error
})
}
// set age
setSome({
age:13
})
// set name
setSome({
name:'bob2'
})
But it doesn't work. What to do?
info:
Element implicitly has an 'any' type because expression of type 'string' can't be used to index type 'Person'. No index signature with a parameter of type 'string' was found on type 'Person'.
CodePudding user response:
Not exactly sure what's your goal from the question but I think what you need is Objects as Maps.
This way you can index an object to a key of any type you set.
I actually don't know if this is what you're asking for but I have a small snippet in my notes: https://github.com/Stratis-Dermanoutsos/Full-Stack-Notes/tree/main/Languages/Frontend/TypeScript#objects-as-maps
CodePudding user response:
It's because you are assigning a string key to the partial of your custom Person interface, but Typescript transpiler doesn't have the information that your Person interface are composed of string key and value pairs. To make this happen you should tell the transpiler that your Person object has string keys. So basically you can explicitly create keyvalue interface:
interface KeyValue{
[key:string]:any
}
Or you can use built-in Typescript types doesn't matter. The complete code is;
interface KeyValue{
[key:string]:any
}
interface Person extends KeyValue{
name:string,
age:number
}
function setSome<T extends Person>(obj:T, payload:Partial<T>){
Object.keys(payload).forEach((key)=>{
Object.assign(obj, {[key]: payload[key]});
})
return obj;
}
let bob:Person = {
name:'bob',
age:12
}
const returner = setSome(
bob,
{age:13},
)
console.log(returner);
CodePudding user response:
The root cause of the ts error is that:
Object.keys(payload)
returns a string
type, rather than (keyof T)[]
.
Typescript does that intentionally, because you can something like this without any error:
interface Person {
name:string,
age:number
}
const person = {
name:'bob',
age:12,
randomKey: 123, // <-- you can set any extra keys
}
function takePerson(person: Person) {
return Object.keys(person);
}
takePerson(person); // <-- returns ["name", "age", "randomKey"]!
More context around can be found: https://stackoverflow.com/a/55012175/9634568
I don't recommend to add the any
index signature as other answers have suggested, unless you could set arbitrary keys at runtime. Because it will make your type check work unexpectedly, if you don't understand how it works. For example, a playground shows how getter and setter use cases cannot catch typos:
interface Person {
name:string,
age:number,
[key: string]: any
}
const bob:Person = {
name:'bob',
age:12,
agee: "typo" // <- no ts error any more
}
bob.agee // <- no ts error any more
In your example, you can just simply do this as shown in this playground:
function setSome<T extends Person>(payload:Partial<T>){
Object.assign(bob, payload);
}
// Or this, if you need to do more things before setting values
function setSome2<T extends Person>(payload:Partial<T>){
for (let key in payload) {
// do something here, e.g., filter key
Object.assign(bob, { [key]: payload[key] });
}
}