4

Currently working with Slacks API and there are instances where I send JSON requests with strings, and these strings return at a later point as property names.

I would like have an interface and send one of its property names as a string. And then have the returning object correctly typed. I dont want to have to deal with "magic strings" or constants that I have to keep in sync with the interface.

Quick example:

// This is the request I send out to Slack
const request = {
    actionId: "specialProperty"
};

// And Slack might give me this object at a later point
const incomingWebhook = {
    specialProperty: "Value I want to read"
}

I can fairly easily get typing for this with an interface

interface SpecialPropertyInterface {
  specialProperty: string;
}

My issue is that this interface is bound to the string that I send out.

Is there a way for me to get the key/property "specialProperty" from my SpecialPropertyInterface as a string?

Thorbear
  • 113
  • 2
  • 10
  • 1
    https://stackoverflow.com/questions/43909566/get-keys-of-a-typescript-interface-as-array-of-strings – Muirik Aug 13 '20 at 15:29

2 Answers2

2

I ended up resolving my issue by using "keyof". Not optimal, but I get a typesafe string that is based on a interface property.

I had two nested keys, so I split them up into two interfaces and and used keyof to get the string of the property for each of them.

export interface HoursBlock {
  hours: HoursBlockAction;
}

export interface HoursBlockAction {
  hoursAction: {
    // eslint-disable-next-line camelcase
    selected_option: {
      value: string;
    };
  };
}

...

// This string will only be valid if you write the name of the property.
const hoursBlockId: keyof HoursBlock = "hours";
const hoursActionId: keyof HoursBlockAction = "hoursAction";

// If you type a different string you will trigger an error.
// Type '"wrong"' is not assignable to type '"hours"'.ts(2322)
const wrongHoursBlockId: keyof HoursBlock = "wrong";
Thorbear
  • 113
  • 2
  • 10
  • 2
    If you don't want to split the interfaces, you can write the following: `const hoursActionId: keyof HoursBlock['hours'] = "hoursAction";` – gianlucaparadise Mar 25 '22 at 10:15
1

Here is an attempt.

First, add as const as a suffix to the declaration of your request object:

const request = {
    actionId: "specialProperty"
} as const;

As a consequence, the type of the actionId property is a literal ("specialProperty") instead of a string:

type RequestActionId = typeof request["actionId"] // "specialProperty"

Now, we can use it in a mapped index signature:

type SpecialPropertyInterface = {
  [propName in RequestActionId]: string; // specialProperty: string
}

Playground Link

Paleo
  • 21,831
  • 4
  • 65
  • 76