What is the best way to structure tabular data with metadata using TypeScript?

Our backend provides data in a specific format, with a data section containing tabular data and a meta section describing the columns in the table. The metadata includes information about the type of each column.

For Example

{
 meta: [
  {name: "foo", type: "NUMBER"},
  {name: "bar", type: "STRING"},
  {name: "baz", type: "TIMESTAMP"},
 ],
  data:[
    [1, "a", 12121232],
    [2, "b", 12121232],
    [3, "c", 12121232],
  ]
}

Is there a way to establish the relationship between the meta and data using TypeScript?

The objective is to successfully typecheck this function, allowing the use of type information from the meta section instead of checking the content of each table cell:

const fn = (data:Data) => {
  if(data.meta[1].type ==='STRING'){
    data.data[0][1].concat('-bar')
  }
}

Answer №1

Recently, I crafted a similar piece of code. Drawing inspiration from that, here is my take on your specific scenario (playground):

type FieldType = 'NUMBER' | 'STRING' | 'TIMESTAMP';

// Disregard the warning related to the unused type parameter `S`
// eslint-disable-next-line @typescript-eslint/no-unused-vars
interface TypedField<T extends FieldType, S> {
  name: string;
  type: T;
}

type NumberField = TypedField<'NUMBER', number>;
type StringField = TypedField<'STRING', string>;
type TimestampField = TypedField<'TIMESTAMP', number>;

type Field = NumberField | StringField | TimestampField;

type TypeofField<F> = F extends TypedField<infer T, infer S> ? TypedField<T, S> extends Field ? S : never : never;

interface Data {
  meta: Array<Field>;
  data: Array<{ [K in Extract<keyof this['meta'], number>]: TypeofField<this['meta'][K]>; }>;
}

const data: Data = {
  meta: [
    { name: "foo", type: "NUMBER" },
    { name: "bar", type: "STRING" },
    { name: "baz", type: "TIMESTAMP" },
  ],
  data: [
    [ 1, "a", 12121232 ],
    [ 2, "b", 12121232 ],
    [ 3, "c", 12121232 ],
  ],
};

Answer №2

To tackle this issue, the key is to utilize the concept of type predicates. By recognizing that data[0][0] is a string when meta[0].type is a string, we can instruct Typescript to treat it strictly as a string. This can be achieved by adapting @MTCoters answer with the application of as:

type FieldType = 'NUMBER' | 'STRING' | 'TIMESTAMP';

// Disregard warning about unused type parameter `S`
// eslint-disable-next-line @typescript-eslint/no-unused-vars
interface TypedField<T extends FieldType, S> {
  name: string;
  type: T;
}

type NumberField = TypedField<'NUMBER', number>;
type StringField = TypedField<'STRING', string>;
type TimestampField = TypedField<'TIMESTAMP', number>;

type Field = NumberField | StringField | TimestampField;

type TypeofField<F> = F extends TypedField<infer T, infer S> ? TypedField<T, S> extends Field ? S : never : never;

interface Data {
  meta: Array<Field>;
  data: Array<{ [K in Extract<keyof this['meta'], number>]: TypeofField<this['meta'][K]>; }>;
}

const data: Data = {
  meta: [
    { name: "foo", type: "NUMBER" },
    { name: "bar", type: "STRING" },
    { name: "baz", type: "TIMESTAMP" },
  ],
  data:[
    [ 1, "a", 12121232 ],
    [ 2, "b", 12121232 ],
    [ 3, "c", 12121232 ],
  ],
};

const fn = (data:Data) => {
  if(data.meta[1].type ==='STRING'){
    (data.data[0][1] as String).concat('-bar')
  }
}

It is important to note that the functionality may break if the as String part is removed.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Is it possible to utilize useEffect for verifying the existence of the user token within the localStorage?

I am in the process of developing a web application that requires authentication. I am wondering if it is effective to create a private route by adding a condition in the useEffect hook of one of my pages. The idea is to check if a token is present before ...

Strategies for managing the fallback scenario while utilizing 'createReducer' to generate a reducer and 'on' to manage actions

In the past, our reducers were created like this before the createReducer helper method was introduced: export function reducer(state: AppState, action: Action) { switch (action.type) { case "[Category List] Add Category": return { . ...

Error detected in Deno project's tsconfig.json file, spreading into other project files - yet code executes without issues?

I am working on a Deno project and need to utilize the ES2019 flatMap() method on an array. To do this, I have created a tsconfig.json file with the following configuration: { "compilerOptions": { "target": "es5", ...

Is it necessary to specify a placeholder for the reusable input in React when using typescript?

I'm currently developing a React components library using Typescript and I have a query. Must every single input property be declared in the type/interface when creating a reusable component? For instance, I have an input field created with styled co ...

When Typecasted in Typescript, the result is consistently returned as "object"

Consider a scenario where there are two interfaces with identical members 'id' and 'name': export interface InterfaceA { id: number; name: string; //some other members } export interface InterfaceB { id: number; nam ...

Definitions for images in the following format

I am currently utilizing typescript in conjunction with NextJs and next-images. Here is the code snippet: import css from "./style.sass"; import img from './logo.svg'; import Link from 'next/link'; export default () => <Link hre ...

Divide the code into individual components within Angular 2 projects

I currently have 3 Angular 2 projects developed in TypeScript. Each project contains the same models and services. I would like to find a way to integrate these common elements at a global level and connect them with each individual project. Any suggesti ...

Issue: The variable 'HelloWorld' has been declared but not utilized

Why am I encountering an error when using TypeScript, Composition API, and Pug templating together in Vue 3? How do I resolve this issue when importing a component with the Composition API and using it in a Pug template? ...

Error: Module not found '!raw-loader!@types/lodash/common/array.d.ts' or its type declarations are missing

I encountered a typescript error while building my NEXT JS application. The error message was: Type error: Cannot find module '!raw-loader!@types/lodash/common/array.d.ts' Below is the content of my tsConfig.json file: { "compilerOptions& ...

What are the steps to utilize a personalized validation access form within a component?

I created a unique validator to verify if an email already exists in the database before saving a new user, however, it appears that the validator is not functioning properly. Here is my template: <form class="forms-sample" #f="ngForm" (ngSubmit)="onS ...

Angular 2: Issue with disabled functionality not functioning correctly

In my TypeScript code, I have created a boolean variable called readOnlyMode. When this variable is set to true, all elements should be disabled. To achieve this, I am using [disabled]="readOnlyMode" in the HTML for elements that need to be disabled. Howev ...

Having trouble setting up Nuxt with Typescript integration

I am venturing into the world of Typescript with Nuxt (version 2.6.1) for the first time. After creating a new project using create-nuxt-app, I followed the official guide for Typescript Support. npx create-nuxt-app my-first-app cd my-first-app npm instal ...

Setting attributes within an object by looping through its keys

I define an enum called REPORT_PARAMETERS: enum REPORT_PARAMETERS { DEFECT_CODE = 'DEFECT_CODE', ORGANIZATION = 'ORGANIZATION' } In addition, I have a Form interface and two objects - form and formMappers that utilize the REPOR ...

Attempting to create a promise for a dropdown menu in React-Select

I am facing an issue here: type Person = { value: string; label: string; }; Furthermore, I have a promise-containing code block that fetches data from an API and transforms it into the appropriate array type for a React component. My intention is to r ...

What is the reason for injecting a service into 2 modules following the Singleton Pattern?

Here is the current file situation: AppService AppModule AModule AComponent BModule BComponent Regarding the Service, I have noticed that Angular will create two separate instances of the service if it is injected into two compone ...

What is the best way to showcase the outcome of a function on the user interface in Angular 2?

The code snippet below was originally in my .component.html file: <div class="someContainer"> <div class="text--bold">Display this please:</div> <div>{{ myObject.date ? '2 Jun' : 'Now' }}</div&g ...

Struggling to make even the most basic example work with TypeScript and npm modules

After stumbling upon this repository that made using npm modules within a Typescript program look easy, I decided to give it a try by forking it and making some changes. My goal was to add another package to get a better understanding of the process. So, I ...

Creating a searchable and filterable singleSelect column in the MUI DataGrid: A step-by-step guide

After three days of working on this, I feel like I'm going in circles. My current task involves fetching data from two API sources (json files) using the useEffect hook and storing them in an array. This array contains a large number of products and a ...

The Angular component refuses to open

Having some trouble with my navbar containing different components that should open upon selection. The profile component is opening correctly, but the "My favorites" button isn't displaying anything from that component. Here's the code snippet ...

Icon for closing Mui Snackbar

I am facing an issue with my notification component that uses the mui snackbar to display alerts. I want to display multiple notifications stacked vertically, but when I try to close one notification using the "Close" icon, it ends up closing both that o ...