I've encountered a challenge while transitioning my react-native project from Flow to TypeScript. The stumbling block is recreating this specific type from Flow:
declare type ApolloData<T, nodeName: string = 'node'> = {
[nodeName]: ?T,
viewer?: ?Viewer,
placeSearch?: ?PlaceConnection,
contactIqLookup?: ?ContactIq,
};
This structure allowed me to type my GraphQL data like this:
const data: ApolloData<Space> = fetchData();
const space: Space = data.node;
// OR
const data: ApolloData<Space, 'space'> = fetchData();
const space: Space = data.space;
In my attempt to replicate this in TypeScript, I initially wrote this:
type ApolloData<T, nodeName extends string = 'node'> = {
[node: nodeName]: T | null;
viewer?: Viewer | null;
placeSearch?: PlaceConnection | null;
contactIqLookup?: ContactIq | null;
}
Unfortunately, this led to an error:
TS1023: An index signature parameter type must be 'string' or 'number'.
Further research introduced me to the Record
type, which seemed promising. My revised attempt looked like this:
type ApolloData<T, nodeName extends string = 'node'> =
Record<nodeName, T | null> &
{
viewer?: Viewer | null;
placeSearch?: PlaceConnection | null;
contactIqLookup?: ContactIq | null;
}
However, a drawback of this approach is that the other properties are typed as viewer: Viewer | null | T
instead of just Viewer | null
due to the Record
type handling all object properties.
Is there a way in TypeScript to accommodate a generic parameterized key and value while also incorporating other fields?