If the goal is to ensure that the second type parameter is a subset of the original at any given level, you can implement this using DeepPartial
as a constraint on the second parameter for EnsureSubInterface
type DeepPartial<T> = {
[P in keyof T]?: T[P] extends Array<infer U>
? Array<DeepPartial<U>>
: T[P] extends ReadonlyArray<infer U>
? ReadonlyArray<DeepPartial<U>>
: DeepPartial<T[P]>
};
type EnsureSubInterface<T, U extends DeepPartial<T>> = U
interface Person {
age: number,
name: string,
hometown?: {
city: string,
zip: number
}
}
type SubPerson = EnsureSubInterface<Person, {
name: string,
hometown: {
city: string,
}
}>
type NotSubPerson = EnsureSubInterface<Person, {
name: string,
hometown: {
city: number, // error
}
}>
type NotSubPerson = EnsureSubInterface<Person, {
name: string,
hometown: {
City: string, // error
}
}>
Depending on your tslint config you might want to make these modifications:
type DeepPartial<T> = {
[P in keyof T]?: T[P] extends (infer U)[]
? DeepPartial<U>[]
: T[P] extends ReadonlyArray<infer U>
? ReadonlyArray<DeepPartial<U>>
: DeepPartial<T[P]>
};