Search code examples
javascripttypescriptinterfacetypescript-types

Typescript Interface, reference one instance property for dynamic second property


I want to use a property within a Typescript Interface's instance to compute a second property. I've been looking into Generics but I'm not successfully implementing this yet. :

Here's an example

// Here I define all valid names - works fine!

type ModalNames = 'payment' | 'source'

// This interface implements all expected payloads
//  for each name 

interface ModalPayloads {
  payment: {
    plan: string
  }
  source: {
    metadataId: string
  }
}


// How do I use the instance name
//  to grab the correct payload property?
export interface ShowModalPayload {
  name: ModalNames
  modalProps?: ModalPayloads[instance.name]
}

In other words, for a given instance of ShowModalPayload, I want to assign modalProps to a type derived from instance.name. Hopefully this makes sense!


Solution

  • I'd use generics for this:

    export interface ShowModalPayload<K extends ModalNames> {
      name: K
      modalProps?: ModalPayloads[K]
    }
    

    Note the use of ModalPayloads[K] in place of ModalPayloads[instance.name]. That's called a lookup type and it allows you to describe the type of m[k] where m has type ModalPayloads and k has type K.

    Anyway the ShowModalPayload interface is now generic in K, which should be one of the ModalNames literals:

    declare const smpPayment: ShowModalPayload<'payment'>;
    smpPayment.name // "payment"
    smpPayment.modalProps // {plan: string} | undefined
    
    declare const smpSource: ShowModalPayload<'source'>;
    smpSource.name // "source"
    smpSource.modalProps // {metadataId: string} | undefined
    

    Those correspond to what you're looking for, I think. Note that the following is still possible:

    declare const smpWhoKnows: ShowModalPayload<ModalNames>
    smpWhoKnows.name // ModalNames
    smpWhoKnows.modalProps // {plan: string} | {metadataId: string} | undefined
    

    which is possibly not what you want, but is not prohibited. The above definition is probably sufficient for most use cases, though.

    Hope that helps. Good luck!