Home > Enterprise >  How to force generic type param to conform to a certain signature
How to force generic type param to conform to a certain signature

Time:03-20

I want to put a <T extends JsonRequest> on a function signature to force any interfaces to be serializable.

The error I get when trying this is:

Type 'AlertHistoryResponse' does not satisfy the constraint 'JsonRequest'.
  Type 'AlertHistoryResponse' is not assignable to type 'JsonObject'.
    Index signature for type 'string' is missing in type 'AlertHistoryResponse'.(2344)

I don't want to add an index signature to AlertHistoryResponse though; is there any way I can assert AlertHistoryResponse is a JsonRequest?


Code:

type JsonPrimitive = string | number | boolean | null;
type JsonObject = {
    [key: string]: JsonRequest | undefined  // allow undefined here because these keys will be stripped when stringified
}
type JsonMap = Map<string, JsonRequest>  // will be converted into an object
type JsonArray = Array<JsonRequest>;
type JsonSet = Set<JsonPrimitive>  // will be converted into an array

/**
 * Represents an object that can be serialized with jsonStringify() (not the native JSON.stringify)
 */
export type JsonRequest = JsonPrimitive | JsonObject | JsonArray | JsonSet | JsonMap

function createGetQuery<T extends JsonRequest>(route: string) {
    return null
}

interface AlertHistoryEntry {
    id: string,
    segmentNumber: number
    alert: string
    dateUtc: string
    user: string
}

interface AlertHistoryResponse {
    log: AlertHistoryEntry[]
    timezone?: string
}

export const useAlertHistoryQuery = createGetQuery<AlertHistoryResponse>('bookings.getAlertHistory')

playground

CodePudding user response:

An interface won't satisfy the index signature requirement because it can have new fields added to it (open). Use a type alias instead (closed). See Differences Between Type Aliases and Interfaces in the TS handbook.

TS Playground

type JsonSerializable = boolean | number | null | string | JsonSerializable[] | { [key: string]: JsonSerializable; };

type JsonMap = Map<string, JsonSerializable>  // will be converted into an object
type JsonSet = Set<JsonSerializable>  // will be converted into an array

type Serializable = JsonSerializable | JsonMap | JsonSet;

function createGetQuery <T extends Serializable>(route: string) {
  return null;
}

type AlertHistoryEntry = {
  id: string;
  segmentNumber: number;
  alert: string;
  dateUtc: string;
  user: string;
};

type AlertHistoryResponse = {
  log: AlertHistoryEntry[];
  timezone?: string;
};

const useAlertHistoryQuery = createGetQuery<AlertHistoryResponse>('bookings.getAlertHistory');

  • Related