So I've created a merge function which takes in an object & a key of another object of objects. The function then returns the merged objects as below.
const data = {
end: 'World'
}
const working = {
test: 'Worked'
}
const map = {
data,
working
}
export type Map = typeof map;
export type Keys = keyof Map;
function merge<A, B extends Keys, C extends Map[B] & A>(input: {
obj: A;
append?: B;
}) {
const append = input.append ? map[input.append] : {};
return {
...input.obj,
...append
} as C
}
Now this works perfectly fine when the map object has > 1 property. aka
const initial = {
start: 'Hello'
}
const a = merge({
obj: initial,
append: 'data'
);
const b = merge({
obj: initial
});
// Available as expected
console.log(a.end);
// Errors as expected
console.log(b.end);
However if I change the map object to only have 1 property like so
const map = {
data
}
Then this occurs
const a = merge({
obj: initial,
append: 'data'
);
const b = merge({
obj: initial
});
// Available as expected
console.log(a.end);
// Available but outputs undefined
console.log(b.end);
I kind of understand what is going on. When the map object only has 1 property, the generic is defaulting to that key so TypeScript believes the object is merged when it isn't.
How do I avoid this?
export type Map = typeof map;
export type Keys = keyof Map;
function merge<A, B extends C extends keyof Map ? A & Map[C] : A, C extends undefined | keyof Map = undefined>(input: {
obj: A;
append?: C;
}) {
const append = input.append ? map[input.append as keyof Map] : {};
return {
...input.obj,
...append
} as B
}
const data = {
end: 'World'
}
const map = {
data
}
// Testing
const start = {
hello: 'World'
}
const a = merge({
obj: start
});
// Outputs as expected
console.log(`${a.hello} World`);
// Errors as expected
console.log(`${a.hello} ${a.end}`);
const b = merge({
obj: start,
append: 'data'
})
// Outputs as expected
console.log(`${b.hello} ${b.end}`);
So this is how I've gotten it working.
Basically C extends undefined | keyof Map and has a default type of undefined so by default the returned type is A & undefined. When a value which must be keyof Map is passed as the append then C becomes that type and thus the returned type is Map[C] & A