gligoran
gligoran

Reputation: 3338

Convert one object's keys and values into a new type

I'm working with a platform that requires me to define an object like this:

interface Source {
  params: Params,
  resolve: (props: ?) => string;
}

const source: Source = {
  params,
  resolve,
}

The params object is defined like this:

interface Params {
  [key: string]: 'text' | 'number';
}

and here's an example of it:

const params: Params = {
  canonical_url: 'text',
  published: 'text',
};

This object describes which and what types of params get passed to the resolve function:

const resolve: string = (props: ?) => {...};

What I'd like to achieve is that the type of props for the resolve function is derived from the params object. For the upper example, the resulting type would be:

interface Props {
  canonical_url: string,
  published: string,  ​
}

The other option would be for this to work the other way round. So that the Props type defines the object for params, but that would mean that TS has to create an actual JS object from a type at transpile time, which I'm not sure is possible.

Upvotes: 0

Views: 49

Answers (1)

koorosh
koorosh

Reputation: 414

You need to define your interfaces as generics:

type Text_ = "text";
type Number_ = "number";

interface Params<T extends Text_ | Number_> {
  [key: string]: T;
}

interface Source<
  T extends Text_ | Number_,
  // this is where we conditionally map 'text' type literal to string type and 'number' to literal to number type
  U = T extends Text_ ? string : T extends Number_ ? number : unknown 
> {
  params: Params<T>;
  resolve: (props: U) => string;
}

const params: Params<Text_> = {
  canonical_url: "text",
  published: "text"
};

const source: Source<Text_> = {
  params,
  resolve: (props) => ""
};

source.resolve(""); // accepts string
source.resolve(1); // number is prohibited

Codesandbox example: https://codesandbox.io/s/unruffled-murdock-eoplg?file=/src/index.ts:0-527

Upvotes: 1

Related Questions