Home > Blockchain >  Typescript: Define type which holds functions for every key another type
Typescript: Define type which holds functions for every key another type

Time:09-28

I need to define two types:

  1. Type for object which holds some data with values of defined types
  2. Type for object which holds validators for the values contained in object with the first type

My realization:

type Data = Record<string, string|number|Array<string>>
type Validators<T extends Data> = {
  [P in keyof T]?: (value: T[P]) => string
}

interface MyData extends Data {
  first: string,
  second: number,
  third: Array<string>,
}

const validators: Validators<MyData> = {
  first: (value: string) => "",
};

With this realization i get the error for the validators object:

TS2322: Type '{ first: (value: string) => string; }' is not assignable to type 'Validators'.   
Property 'first' is incompatible with index signature. 
   Type '(value: string) => string' is not assignable to type '(value: string | number | string[]) => string'.       
     Types of parameters 'value' and 'value' are incompatible. 
        Type 'string | number | string[]' is not assignable to type 'string'. 
          Type 'number' is not assignable to type 'string'.

Is it possible to define such types in typescript?

CodePudding user response:

Is it possible to define such types in typescript?

Yes. The problem is that by extending Data, MyData now has a string index that returns string | number | Array<string>:

// as if you had written this
interface MyData {
  first: string,
  second: number,
  third: Array<string>,
  [k: string]: string | number | Array<string>
}

When building the Validator, the first key matches both your first key and your string index.

Because of that, typescript expects a function that matches (value: string) => string, but also matches (value: string | number | Array<string>) => string.

Since you're providing a function that only accepts a string as a parameter, the compilation breaks.

If you want to apply the type restriction of string | number | Array<string> to the interfaces that use Validators, you should not use a string index, but a index that just uses the subtype keys:

type Data<Subtype> = Record<keyof Subtype, string | number | Array<string>>
type Validators<Subtype extends Data<Subtype>> = {
    [P in keyof Subtype]?: (value: Subtype[P]) => string
}

interface MyData {
    first: string
    second: number
    third: Array<string>
}

const validators: Validators<MyData> = {
    first: (value: string) => "",
};
  • Related