1

I'd like to type a complex object with some fields based on the types of its other fields. Is the following almost-real code possible?

interface CafeSpec {
  menu: { [key: string]: number }; // Suppose we could call this type Items
  special_disposal: { [K in keyof Partial<Items>]: string } 
}

let bridgeLocation: CafeSpec = {
  menu: {
    cake: 5,
    toast: 20
  },
  special_disposal: {
    cake: "eat",
    flowers: "take home"  // This should error! flowers not key of Items
  }
}

I'm not interested in any solutions that require writing out the menu items more than once so explicitly declaring a type for the menu for a generic is out of the question.

My best solution so far is with a function. However, this is still a little more verbose than I'd like.

interface Menu {
    [key: string]: number;
}

interface CafeSpec<M extends Menu> {
    menu: M;
    special_disposal: { [K in keyof Partial<M>]: string};
}

type PartialSpec<M extends Menu> = Omit<CafeSpec<M>, "menu">

function createCafe<M extends Menu>(menu: M, extra: PartialSpec<M>): CafeSpec<M> {
    return {...extra, menu};
}

let newLocation = createCafe(
  {
    cake: 5,
    toast: 20
  },
  {
    special_disposal: 
    {
        cake: "eat",
        flowers: "take home" // Nice error here shown below
    }
  }
);
Object literal may only specify known properties,
 and 'flowers' does not exist in type 
 '{ cake?: string | undefined; toast?: string | undefined; }'
John Jeng
  • 41
  • 3
  • This seems to be a really similar question: https://stackoverflow.com/questions/54834373/set-type-based-on-sibling-values-type – DAG Aug 16 '19 at 08:31
  • 1
    Thanks! I hadn't seen that question. Using the knowledge from that question, it looks like I'd have to write `interface Entity {cake: number; pie: number; scones: number;}` and then later, write those same keys again when I define the numbers ie. `entity: {cake: 1, pie: 13, scones: 2},` I'd like to avoid having to do that. – John Jeng Aug 16 '19 at 22:11

0 Answers0