stahlhammer
stahlhammer

Reputation: 107

Check if a type exists as a property of another type

I have some types:

interface IMyParentInterface {
    foo: string,
    items: IMyChildinterface[]
}

interface IMyChildinterface {
   id: number,
   bar: string,
}

And I have a class which uses the interfaces:

class MyClass<T, D = keyof T> {...}

So, I'm creating the instance:

const class = new MyClass<IMyParentInterface, IMyChildinterface>(...)

So I want to strictly check if IMyChildinterface is that exact type, that is used as a IMyParentInterface property.

In other words, I shouldn't be able to do something like this:

const class = new MyClass<IMyParentInterface, TJustRandomType>(...)

Unfortunatelly, typeof does nothing to make it work

Upvotes: 1

Views: 1524

Answers (1)

Maciej Sikora
Maciej Sikora

Reputation: 20132

We need to restrict types to specific subsets by extends keyword. Consider:

class MyClass<
T extends { items: any }, 
D extends T['items'][number] = T['items'][number]> { };

const a = new MyClass<IMyParentInterface, IMyChildinterface>(); //ok

interface Other {
    a: string
}
const b = new MyClass<IMyParentInterface, Other>(); //error

Explanation:

  • T extends { items: any } as you put there generic, I assume IMyParentInterface is not one and only type you want to use. I am then restricting that our first type has items property
  • D extends T['items'][number] = T['items'][number] - restriction that second type will be the type from the first items property item type.
  • Take a look that now putting the second type is not nesesery as it will be by default what you want

More generic solution with picking the wanted key:

// proposition with picking the property
class MyClass<
Prop extends PropertyKey, 
T extends Record<Prop, any>, 
D extends T[Prop][number] = T[Prop][number]> { }
const a = new MyClass<'items', IMyParentInterface, IMyChildinterface>(); //ok

interface Other {
    a: string
}
const b = new MyClass<'items', IMyParentInterface, Other>(); //error

Even more generic with inferring type of array kind properties

// proposition with every array type property
type PickByValue<T, V, _Keys extends keyof T = {
    [K in keyof T]: T[K] extends V ? K : never
}[keyof T]> = Pick<T, _Keys>

class MyClass<
    T extends (Array<any> extends T[keyof T] ? object : never),
    D extends _E[keyof _E][number],
    _E extends Record<any, Array<any>> = PickByValue<T, Array<any>>
    > { }
const a = new MyClass<IMyParentInterface, IMyChildinterface>(); //ok

interface Other {
    a: string
}
const b = new MyClass<IMyParentInterface, Other>(); //error

Upvotes: 2

Related Questions