TypeScript does not allow arbitrary mutation of variable states. Even though there is some limited support for narrowing variables upon assignment, this only occurs if the type of the variable is a union type. So, when you reassign a variable like `payload` with a value of type `X`, it will narrow to that specific type. However, if you try to reassign `payload` with a value of type `X & Y`, no narrowing will take place.
If you aim to work smoothly within the type system, it's best to have each variable maintain a single type throughout its lifetime. Instead of repeatedly reassigning `payload`, consider creating new variables for each assignment:
const payload = {
name: "Aaron"
}
const payload1 = addA(payload)
const payload2 = addB(payload1)
const result = handler(payload2)
// const result: { name: string; } & ExtA & ExtB
If you truly require a variable whose type narrows over time, you can achieve this using assertion functions, but keep in mind that they come with certain limitations. These functions modify their inputs and cannot return any values, so reassignment will not narrow the variable as intended. Here's an example implementation:
function addA<T extends Base>(payload: T): asserts payload is T & ExtA {
Object.assign(payload, {
address: "123 Fake St"
});
}
function addB<T extends Base>(payload: T): asserts payload is T & ExtB {
Object.assign(payload, {
streetName: "Main"
});
}
In this scenario, `addA()` and `addB()` are assertion functions that modify their input directly rather than returning anything.
By utilizing these assertion functions, you can maintain a single `payload` variable while narrowing it with each function call:
const payload = {
name: "Aaron"
}
addA(payload)
addB(payload)
const result = handler(payload)
// const result: { name: string; } & ExtA & ExtB
This approach works effectively, but it's recommended to use it sparingly unless absolutely necessary. If at any point you need to widen the input (like with a hypothetical `removeA()` function), assertion functions won't be suitable since they're designed for narrowing purposes. In such cases, separate variables should be used instead.
Link to Playground code