In my current project, I'm working with a class that utilizes a dictionary to store data. This dictionary can be filled by piping functions that follow a specific format: they take in an object (dictionary), destructure its properties as arguments, and then return a new object with additional properties (that can also be added to the dictionary).
I'm wondering if there is a way to define the type for these pipe functions so that only properties that have already been added to the dictionary can be destructured. Essentially, restricting the access to only existing properties.
type AugmentFun = (source: Record<string, any>) => Record<string, any>;
class Pipeable {
constructor(private dict: Record<string, any>) {}
static of = (dict: Record<string, any>) => new Pipeable(dict);
static augment = (source: Pipeable, fun: AugmentFun) => {
return Pipeable.of(Object.assign({}, source.dict, fun(source.dict)));
};
pipe = (...funs: AugmentFun[]) => funs.reduce(Pipeable.augment, this);
}
const p = new Pipeable({});
// This functionality works without any type errors
const res1 = p.pipe(
() => ({ a: 1, b: 2 }), // { a: 1, b: 2 }
({ a, b }) => ({ c: a + b }), // { a: 1, b: 2, c: 3}
({ c }) => ({ d: c ** 2 }) // { a: 1, b: 2, c: 3, d: 9}
);
// In contrast, this scenario should trigger type errors but it doesn't
const res2 = p.pipe(
() => ({ a: 1, b: 2 }), // { a: 1, b: 2 }
({ c }) => ({ d: c + 10 }) // Expected error: "c" does not exist
);
(the augment()
method is pure, but could potentially mutate the original instance instead, although this aspect is not crucial)