function updateObj<T: Object>(obj: T, fields: $Shape<T>) : T {
return Object.assign({}, obj, fields)
}
const person1 = { name: 'Giulio', age: 42 }
// I guess here Flow infers that person1 is of type { name: string, age: number }
const person2 = updateObj(person1, { age: 43 } ) // ok
const person2 = updateObj(person1, { a: 1 } ) // error: property `a` of object literal. Property not found
const person2 = updateObj(person1, { age: 'a' } ) // error: string. This type is incompatible with number
const person2 = updateObj(person1, { surname: 'Canti' } ) // error: property `surname` of object literal. Property not foundNested structures
/*
type Person = {
name: string,
age: number,
hobbies: {
surf: boolean,
climbing: boolean
}
};
*/
const person1 = {
name: 'Giulio',
age: 42,
hobbies: {
surf: false,
climbing: true
}
}
const person2 = updateObj(person1, {
hobbies: updateObj(person1.hobbies, {
surf: true // ok
})
})
const person2 = updateObj(person1, {
hobbies: updateObj(person1.hobbies, {
surf: 'a' // error: string. This type is incompatible with boolean
})
})Problem. nullable types
type Person = {
name: string,
age: number,
hobbies?: { // <= note the ? here
surf: boolean,
climbing: boolean
}
};
const person1 = { name: 'Giulio', age: 42 }
const person2 = updateObj(person1, { // error: property `hobbies` of object literal. Property not found
hobbies: updateObj(person1.hobbies, {
surf: true,
climbing: false
})
})In order to tell flow that there is a hobbies property we must annotate person1, but then Flow raises another error
const person1: Person = { name: 'Giulio', age: 42 } // <= annotate with the Person type
const person2 = updateObj(person1, {
hobbies: updateObj(person1.hobbies, {
surf: true,
climbing: false
})
})src/index.js:32
32: hobbies: updateObj(person1.hobbies, {
^ property `climbing` of object literal. Property cannot be assigned on possibly undefined value
32: hobbies: updateObj(person1.hobbies, {
^^^^^^^^^^^^^^^ undefined
src/index.js:32
32: hobbies: updateObj(person1.hobbies, {
^ property `surf` of object literal. Property cannot be assigned on possibly undefined value
32: hobbies: updateObj(person1.hobbies, {
^^^^^^^^^^^^^^^ undefined
One option would be to change the updateObj signature using ?T instead of T and returning fields when obj is nully
function updateObj<T: Object>(obj: ?T, fields: $Shape<T>): T {
return obj ?
Object.assign({}, obj, fields) :
fields
}now it works
const person1: Person = { name: 'Giulio', age: 42 }
const person2 = updateObj(person1, {
hobbies: updateObj(person1.hobbies, {
surf: true,
climbing: false
})
})
const person2 = updateObj(person1, {
hobbies: updateObj(person1.hobbies, {
surf: 'a' // error: This type is incompatible with boolean
climbing: false
})
})but unfortunately using $Shape I loose a bit of type safety (seems a known issue with Flow)
const person2 = updateObj(person1, {
hobbies: updateObj(person1.hobbies, {
surf: true
// <= climbing is missing but Flow doesn't raise any error
})
})