I opened an issue in the TypeScript repo about conditional mapped types and generic type parameters. I was trying to define a mapped type SubAddsNewPropsToSuper<Sub, Super>
which is kind of like the extends
constraint for generic types except it only allows Sub to add new properties to Super while not changing the type of any existing property.
The question is if it is possible to define such a type. First goes my code example, second is the expected behaviour and third is the actual behaviour.
// defines a type that contains all properties of Super exactly as they are defined in Super
// and all properties of Sub that are not already defined in Super.
type SubAddsNewPropsToSuper<Sub, Super> = { [KeyOfNewPropsInSub in Exclude<keyof Sub, keyof Super>]: Sub[KeyOfNewPropsInSub] } & Super;
class Controller<TModel> {
constructor(public model: SubAddsNewPropsToSuper<TModel, { Title: string }>) { }
setFancyTitle() {
this.model.Title = "FANCY"; // works
this.updateModel({ Title: "FANCY" }); // does not work - why?
const x: Partial<SubAddsNewPropsToSuper<{ Title: "Foo" }, { Title: string }>> = { Title: "FANCY" }; // works
const y: Partial<SubAddsNewPropsToSuper<TModel, { Title: string }>> = { Title: "FANCY" }; // does not work - why?
}
updateModel(changes: Partial<SubAddsNewPropsToSuper<TModel, { Title: string }>>) {
// merge "changes" into "this.model"
}
}
Expected behavior:
The type SubAddsNewPropsToSuper<A, B>
should define a type that contains all properties of type B
exactly as they are defined in B
even if type A
defines the same property with a different type (e.g. changes the type of a property from string
to "OnlyThisString"
).
The idea behind this is to circumvent the "problem" if Controller
was defined as class Controller<TModel extends { Title: string }>{ ... }
in which case TModel
might actually change the type of property Title
.
Actual behavior:
It works as expected if A
and B
are both non-generic types but it does not work if A
is a generic type.