0

I am looking for a way given a interface:

interface Person {
  age: number,
  name: string,
  hometown?: {
    city: string,
    zip: number
  }
}

type SubPerson = EnsureSubInterface<Person, {
  name: string
}>

this would be valid:

const x: SubPerson {
  name: "Tom"
}

this would be invalid:

const x: SubPerson {
  age: 12
}
ThomasReggi
  • 55,053
  • 85
  • 237
  • 424

1 Answers1

1

If you just want to ensure that the second type parameter is a subset of the original at any give level, you could use DeepPartial (from here) as a constraint on the second parameter to 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 this:

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]>
};
ThomasReggi
  • 55,053
  • 85
  • 237
  • 424
Titian Cernicova-Dragomir
  • 230,986
  • 31
  • 415
  • 357