Home > Net >  How do you decide the type of data to be returned in typescript based on the actual value of the sch
How do you decide the type of data to be returned in typescript based on the actual value of the sch

Time:05-28

I have a code setup like this.

I want the returned data to have a dynamic type constraint.

type Schema = ('id' | 'name' | 'description' | 'content' | 'createdAt')[];

type Obj = {
  id: number;
  name: string;
  description: string;
  content: string;
  createdAt: Date;
}

function getBySchema(schema: Schema) {
  const result = /* Get the object of the specified field from somewhere according to the schema */
  return result;
}

const picked = getBySchema(["id", "name"]);

// Expect it to pass.
picked.name
// Expect it not to pass.
picked.createdAt

I specify Schema in the argument type of the function to get an input prompt.

schema prompt

How do I dynamically calculate the type of result based on the actual value of the schema?

I have tried the following, but it seems that it makes judgments based on all the types in the schema.

type PickBySchema<T extends Record<string, unknown>, S extends Schema> = S extends (infer I)[] ? I extends string  ? {
  [K in I]: T[K]
} : never : never;

CodePudding user response:

You can use combination of ReadonlyArray and <infer U> ? U : never to infer the passed down tuple of keys as follows:

function getBySchema<T extends Schema>(schema: T): {
  [key in (T extends ReadonlyArray<infer U> ? U : never)]: Obj[key]} {

Explanation:

  • T extends Schema makes sure that the passed in argument for schema: T is of type Schema.
  • To use the passed in schema entries as the returned object keys we will first need to create an interface using Mapped types where keys are derived from a union of the Schema strings that were passed in. For this we start the interface with key in as normal, and then instead of referencing the Schema type or its keys, we extend the generic T variable to extract what was passed in. We do this by first asserting that it is a ReadonlyArray to create a union and using infer U we get the smallest possible union type that is valid for T (a.k.a what was passed in) and return that using ? U. : never is used to indicate that if a union cannot be inferred, the key is not a valid value and cannot be created.
  • Once we have derived the union of possible keys, we make the corresponding value type as Obj[key] which ensures that the property value has the correct type respective to the key.

Playground link.

Further improvement to the codebase:

You don't necessarily have to manually type out the keys inside Obj type and assign them to the Schema type. You can automate this by using:

type Schema = (keyof Obj)[];

Playground link.

  • Related