francesco
francesco

Reputation: 329

how to create an interface to be a subset of another interface?

some context : playing with graphql I have pre-defined interfaces for query generated automatically, but the results of any given query is only a subset of that automatically-generated-interface

and to have a compile-time checking of query-results-interfaces I need an interface that is a custom subset ( STRICTLY without any additional parameters ) of a Super-interface,

an example...


type RecursivePartial<T> = {
  [P in keyof T]?: RecursivePartial<T[P]>;
};

interface IAutogenerated {
 name : string
 weight: number
 age: number
}
interface ICustom extends RecursivePartial<IAutogenerated> {
    name: string 
    agez : number  // <------ I want this not allowed at compiletime ( ie: because it's a typo!)
}
let a : ICustom = {
  name : "me" // this is required, weight is not because  the usage of "RecursivePartial"
}

in this example I want ICustom to be exactly a subset of IAutogenerated ( maybe with some creative usage of "keyof"?

in java I would use the override in each member of the extending interface to be sure to not add typo and to have the compiler help me during refactoring..

version of typescript: 3.3

thank you, Francesco

EDIT: given the answer of Tom I've added another ADDITIONAL EXAMPLE which may further expose the real usage of the RecursivePartial


// ------ AUTOGENERATED INTERFACE OF GRAPHQL ----- 
export interface People {
  id: number;
  name: string;
  gender: Gender;
  age: number;
  childs: (Maybe<People>)[];
}

/// CUSTOMIZATION BASED ON SINGLE CREATED QUERY
export type Child = Pick<RecursivePartial<People>,
  'name'
  >
export type PeopleListItem = Pick<RecursivePartial<People>,
  'id'|'name'|'childs' >

// what is needed here something between the Pick ( which allow strict subset ) and RecursivePartial, which allow super-typing of subset elements )
// export interface PeopleListItem extends RecursivePartial<People>{
//   id : number,
//   name : string,
//   childs : Child[] // NOTE --> here Child is a subtype of People
// }

to give some context this will constitute the return type of gql:

query {
people {
 id
 name
 childs {
   name 
 } 
}

here with the commented code I don't have the sub-type strictness of Pick, but i can ovverride members with "Partial"s with the Pick I cannot override elements but I have subtype strictness ..

seems a weird question but those constraint are both required to have a typed object sub-graph

EDIT 2 : I've created a monster that provide some of the requirements but it's very ugly , I'm using typescript since 1 week so please pardon me for the following code.. ( and please help me to find something better )


type RecursivePartial<T> = {
  [P in keyof T]?: RecursivePartial<T[P]>;
};

type RecursivePartialPick<T, K extends keyof T> = {
  [P in K]: RecursivePartial<T[P]>;
};

// ------ AUTOGENERATED INTERFACE OF GRAPHQL -----
export interface People {
  id: number;
  name: string;
  gender: Gender;
  age: number;
  childs: (Maybe<People>)[];
}

/// CUSTOMIZATION BASED ON SINGLE CREATED QUERY
export type Child = RecursivePartialPick<People, 'name' >

// this will stabilize sub-fields Picke'd from the original type (Avoid typing errors and code duplication )
type _PeoplePick = 'id' | 'name' | 'childs';

// override the field with a subtype 
interface _PeopleListItem extends RecursivePartialPick<People,_PeoplePick >{
     childs : Child[] //<<--- note here : no safety against typing errors in "childs" field name ( Except that resulting type is not Child[] but (Maybe<People>)[]; 
}
export type PeopleListItem = Pick<_PeopleListItem,_PeoplePick>
let result : PeopleListItem = {
   name : "" ,
   id : 2 ,
   childs : [{ // <Child[]>
     name : "n"
   }]
}

Upvotes: 2

Views: 890

Answers (1)

Tom Cumming
Tom Cumming

Reputation: 1168

type Custom = Pick<RecursivePartial<IAutogenerated>, 'name' | 'age'>; // OK
type Custom2 = Pick<RecursivePartial<IAutogenerated>, 'name' | 'agez'>; // Type error

Upvotes: 2

Related Questions