rsmidt
rsmidt

Reputation: 79

Ensure Type A extends Type B that has a field which is the value of another object

I would like to ensure that type A has a field of a generic type but the name of that field is defined by the value of a field from another object.

E.g. the function:

interface IndexedContext<K extends string, T> {
  context: T;
  key: K;
}

type TypeB<P, K, T> = {
  // this field should be named after IndexedContext[key]
  P[K] = T;
}

const Consumer =
  <T extends object, K>(context: IndexedContext<K, T>) => <C extends ComponentType<TypeA>, TypeA extends TypeB: C => {
    .....
  };

TypeA (props) should have a field which is the value of the field key in IndexedKey? So that when I use this decorator on a react component and pass a ReactContext to it I can make sure that the props have a field which is the same as the key.

@Consumer({context: MyContext, key: 'myKey'})
class MyClass extends Component<MyProps> {}


interface MyProps {
  // compiler should fail if this key is missing.
  myKey: // Type of value of MyContext
}

Upvotes: 2

Views: 54

Answers (1)

Titian Cernicova-Dragomir
Titian Cernicova-Dragomir

Reputation: 249726

You don't need to define any extra mapped types (such as TypeB sould be in your example), you can use Record to get a mapped type from a string literal and a field type.

You also need to capture the instance type not the constructor. If you write {context: MyContext, key: 'myKey'}, context will be the class MyContext, and so T will be inferred to typeof MyContext not MyContext. To get the instance type you could type context as new (...a:any[]) => T.

Putting it all together:

interface IndexedContext<K extends string, T> {
    context: Type<T>;
    key: K;
}
type Type<T> = new (...a: any[]) => T

const Consumer =
    <T extends object, K extends string>(context: IndexedContext<K, T>) => <C extends ComponentType<Record<K, T>>>(cls: C) => {

    };

class MyContext { }
@Consumer({ context: MyContext, key: 'myKey' })
class MyClass extends Component<MyProps> { }


interface MyProps {
    // compiler WILL fail if this key is missing.
    myKey: MyContext// Type of value of MyContext
}

Note context will have to be assigned a class for this to work, you will not be able to directly use interfaces or primitives.

Upvotes: 1

Related Questions