skip to Main Content

I am quite new to typescript and I am not sure if this is achievable or not.

Basically, I have some fetched data with known types (some queries to database) and I want to store them in a predefined variable so I can pass it around (a context in my use case). However, the data type changes base on the query parameters. I am storing the data inside the variable with type any but the intellisense would be lost.

// file A.ts
export const someContext : { fetchedData: any } = { fetchedData: null }
// file B.ts
import { someContext } from "A"
...
const data = await fetchData("some api and queries")
// data can be {id: string, posts: number} or {id: string, lastLogin: Date} etc
// but the type is known
someContext.fetchedData = data; // there is no intellisense for fetchedData

My question is can I store the data in only one variable while preserving type intellisense.

One solution I thought of is to create a type of all possible fields of the fetched data (based on the database structure), and mark every field as optional but this is quite complicated and I need to update it everytime I change the db structure and I want to know if there is a way without statically setting the types. Ideally, I would want it to work with nested objects as well.

type fetchedDataTypes = {
    id?: number; // statically setting types
    lastLogin?: Date;
    posts?: number;
    ...
}
const someContext : { fetchedData: fetchedDataTypes } = { fetchedData : {} }

EDIT:
For some more context, I am doing something similar to an API call from client to server, and the context is created from the server side and is passed around to different components that is run from the client side, so any data passed into it will persist and can be used across the whole app. The syntax is of course different base on the framework used, but one example similar is nextjs + tRPC, where the end-points for the api calls are typesafe, and I want to find a way to store the data from the api calls without losing the type intellisense, and also without statically setting the types in prior.

A minimal reproducible example using react here.

2

Answers


  1. If you know for sure what your fetch() call will return, you can use generics and Type Assertion to enforce certain return types that will give you intellisense. I’d recommend to define FetchedData as a Union Type instead of optional properties because there’s a difference between a property of value undefined and a property that is not there at all.

    However, you’ll need to be really careful about that since you can trick yourself very easily. In the example below fetchMock will always return {posts: 42}. The return type will only be based on the generic argument.

    type FetchedData =
      | {
          id: number;
        }
      | {
          posts: number;
        }
      | {
          lastLogin: Date;
        };
    
    const someContext: {fetchedData: FetchedData | null} = {
      fetchedData: null
    };
    
    // mock
    async function fetchMock<T extends FetchedData>(): Promise<T> {
      return new Promise(resolve =>
        setTimeout(() => resolve({posts: 42} as T))
      );
    }
    
    (async () => {
      someContext.fetchedData = await fetchMock<{id: number}>();
      console.log(someContext.fetchedData.id);
    
      someContext.fetchedData = await fetchMock<{posts: number}>();
      console.log(someContext.fetchedData.posts);
    
      someContext.fetchedData = await fetchMock<{lastLogin: Date}>();
      console.log(someContext.fetchedData.lastLogin);
    })();
    

    TypeScript Playground

    Login or Signup to reply.
  2. The most straightforward way would be

    const data = await fetchData("some api and queries")
    const someContext = { fetchedData: data }
    

    If you need to create your object before, you may still use the type of data variable:

    const someContext: { fetchedData: null | typeof data } = { fetchedData: null }
    const data = await fetch("some api and queries")
    someContext.fetchedData = data;
    someContext.fetchedData.ok // boolean
    // or
    const someContext: { fetchedData?: typeof data } = { /** fetchedData: undefined */ }
    const data = await fetch("some api and queries")
    someContext.fetchedData = data;
    someContext.fetchedData.ok // boolean
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search