0

I'm converting a project from JS to TS, and a little stuck on the following object:

let campaigns = {
  TOTAL: {
    cost: 3,
    revenue: 6,
    year: 2020,
  },
  campaignId_1: {
    name: "campaignName1",
    cost: 1,
    revenue: 4,
    video: "video1",
  },
  campaignId_2: {
    name: "campaignName2",
    cast: 2,
    revenue: 2,
    video: "video2",
  }
}

In Typescript, I want the object to ALWAYS have the "TOTAL" key with type-A value, and the rest of the keys are dynamic, and should have type-B value.

So first I've defined the child types:

type type_A = {  
  cost: number,
  revenue: number,
  year: number,
}
type type_B = {
  name: string,
  cast: number,
  revenue: number,
  video: string,
}

And now I'm trying to create a type for the parent Campaigns object:

Try 1:

type ICampaigns = {
  TOTAL: type_A,
  [key: string]: type_B, // Hoping it will be handled as "default" or "for all other non-defined keys"
}

But got the following error message: Property 'TOTAL' of type 'type_A' is not assignable to string index type 'type_B'.(2411).

Try 2:

type ICampaigns =  {
  TOTAL: type_A,
} & {
  [key: string]: type_B, // Hoping it will be handled as "default" or "for all other non-defined keys"
}

And now it compiles, but when trying to define such instance I'm getting errors no matter if TOTAL is of type_A or type_B.

let campaigns: ICampaigns = {
  TOTAL: {
    cost: 3,
    revenue: 6,
    year: 2020,
  },
  campaignId_1: {
    name: "campaignName1",
    cost: 1,
    revenue: 4,
    video: "video1",
  },
  campaignId_2: {
    name: "campaignName2",
    cost: 2,
    revenue: 2,
    video: "video2",
  }
}

I know that doing type_A|type_B will solve my problem, but then the TOTAL key will be able to have the type_B object as value, and other dynamic keys can have type_A values, which is not good.

I also know I can convert the Campaigns object to have only defined keys:

let campaigns: ICampaigns = {
  TOTAL: {
    cost: 3,
    revenue: 6,
    year: 2020,
  },
  campaignDetails: { // <--- change
    campaignId_1: {
      name: "campaignName1",
      cost: 1,
      revenue: 4,
      video: "video1",
    },
    campaignId_2: {
      name: "campaignName2",
      cost: 2,
      revenue: 2,
      video: "video2",
    }
  }
}

But that's a lot of converting to do in my project, and also feels like giving up...

A-S
  • 2,547
  • 2
  • 27
  • 37
  • TL;DR version - the following don't work: `type ICampaigns = { defined_required_value: string, [all_other_keys: string]: number, }` or `type ICampaigns = { defined_required_value: string, } & { [all_other_keys: string]: number, } ` – A-S Aug 07 '20 at 11:44

1 Answers1

1

This happens because TOTAL also matches the definition of [key: string]: type_B, but this solution should work for you

Zer0
  • 1,580
  • 10
  • 28
  • The second answer there ([a more specific link](https://stackoverflow.com/questions/57774361/typescript-interface-key-string#answer-57775405)) does work! Thx! (The accepted answer doesn't work) – A-S Aug 07 '20 at 14:17