Generic indexed type value in TypeScript

Viewed 108

Targets

Replace any to appropriate generic in below type definitions:

export type GraphQL_OperationSpecification = {
  variables: GraphQL_OperationVariablesSpecification;
  // off-topic omit
};


export type GraphQL_OperationVariablesSpecification = { 
  [parameterName: string]: GraphQL_OperationVariableSpecification; 
};


export type GraphQL_OperationVariableSpecification = {
  //                                      Here ↓    Here ↓
  transformationFunction?: (requestParameter: any) => any;
  //                                      Here ↓
  mustSubmitIf?: (normalizedRequestParameter: any) => boolean;
    //                                         Here ↓
  transformToNullIf?: (normalizedRequestParameter: any) => boolean;
  // off-topic omit
};

such as below code will be compiled without errors:

export const graphQL_OperationSpecification: GraphQL_OperationSpecification = {
  variables: {
    targetPaginationPageNumber__numerationFrom0: {
      // Assume that API wants page number from 0, not from 1.
      // However here both parameter and return value are number
      transformationFunction(pageNumber__numerationFrom1: number): number { return pageNumber__numerationFrom1 - 1; }
    },
    itemsCountPerPaginationPage: 
      // Assume that we are accepting string from UI but API needs number
      transformationFunction(stringifiedItemsCountPerPaginationPage: string): number { return String(stringifiedItemsCountPerPaginationPage); }
    }
  }
};

Problem

For GraphQL_OperationVariableSpecification, everything is trivial:

export type GraphQL_OperationVariableSpecification<RequestParameterForUI, RequestParameterForAPI> = {
  transformationFunction?: (requestParameter: RequestParameterForUI) => RequestParameterForAPI;
  mustSubmitIf?: (normalizedRequestParameter: RequestParameterForUI) => boolean;
  transformToNullIf?: (normalizedRequestParameter: RequestParameterForUI) => boolean;
};

Problems begins here:

export type GraphQL_OperationVariablesSpecification = { 
  [parameterName: string]: GraphQL_OperationVariableSpecification<RequestParameterForUI, RequestParameterForAPI>; 
};

Now GraphQL_OperationVariablesSpecification needs 2 generic parameters too! But we does not know at advance, which specific type will be the RequestParameterForUI and RequestParameterForAPI.

If we add two parameters to GraphQL_OperationVariablesSpecification, GraphQL_OperationSpecification will required them too. But even if I add generic parameters wherever required:

export type GraphQL_OperationSpecification<RequestParameterForUI, RequestParameterForAPI> = {
  variables: GraphQL_OperationVariablesSpecification<RequestParameterForUI, RequestParameterForAPI>;
};


export type GraphQL_OperationVariablesSpecification<RequestParameterForUI, RequestParameterForAPI> = {
  [parameterName: string]: GraphQL_OperationVariableSpecification<RequestParameterForUI, RequestParameterForAPI>;
};

export const graphQL_OperationSpecification: GraphQL_OperationSpecification<number | string, number> = {
  variables: {
    targetPaginationPageNumber__numerationFrom0: {
      transformationFunction(pageNumber__numerationFrom1: number): number { return pageNumber__numerationFrom1 - 1; }
    },
    itemsCountPerPaginationPage: 
        transformationFunction(stringifiedItemsCountPerPaginationPage: string): number { return String(stringifiedItemsCountPerPaginationPage); }
    }
  }
};

it will not solve the problem:

TS2322: Type '(pageNumber__numerationFrom1: number) => number' is not assignable to
 type '(requestParameter: string | number) => number'.
  Types of parameters 'pageNumber__numerationFrom1' and 'requestParameter' are inco
mpatible.
    Type 'string | number' is not assignable to type 'number'.
      Type 'string' is not assignable to type 'number'.
  > 38 |         transformationFunction(pageNumber__numerationFrom1: number): numbe
r { return pageNumber__numerationFrom1 - 1; }
       |         ^^^^^^^^^^^^^^^^^^^^^^

In the case of pageNumber__numerationFrom1, it's number and nothing else. stringifiedItemsCountPerPaginationPage is string and nothing else. But how to explain it to TypeScript?

1 Answers

I have modified your last snippet so it type-checks: TS Playground link.

But basically, instead of GraphQL_OperationSpecification<number | string, number>, you want GraphQL_OperationSpecification<number, string | number>.

Related