Determine the field's type without using generic type arguments

In my code, there is a type called Component with a generic parameter named Props, which must adhere to the Record<string, any> structure. I am looking to create a type that can accept a component in one property and also include a function that returns the Props for that component. These objects will then be stored in a dictionary format like so:

const components: Record<string, ComponentConfig> = {
  someKey: {
    component: Component1, // Implements the Component<{num: number}> type,
    props() { // Strongly typed Props
      return {
        num: 1
      }
    }
  },
  someOtherKey: {
    component: Component2, // Implements the Component<{str: string}> type,
    props() { // Strongly typed Props
      return {
        str: "test"
      }
    }
  }
}

I attempted a solution using this approach:

type ComponentConfig = never extends infer Props
  ? Props extends Record<string, any>
    ? {
        component: Component<Props>
        props(): Props
      }
    : string // originally never, modified for this example
  : never

However, I encountered an error when assigning an object to a key within the dictionary:

// TS2322: Type { component: Component<{ num: number; }>; props(): { num: number; }; } is not assignable to type  never 
const cfg: Record<string, ComponentConfig> = {
    someKey: {
        component: component1,
        props() {
            return {
                num: 1
            }
        }
    }
}

I am puzzled by this error as 'never' seems to extend everything. The logic dictates that the first ternary expression should always evaluate to true because the empty set is a subset of every set. Thus, depending on the type of Props, the resolved type should either be

{component: Component<Props>; props(): Props}
or string. However, I consistently receive 'never', which confuses me.

I am using these methods to introduce a new type variable Props. If there are more elegant solutions to achieve this, I am open to suggestions.

Initially, I thought about passing it as a generic argument, but that would not work since I would need to specify it while creating the cfg Record. Using any/unknown as a placeholder would lead to incorrect typings for the props function. Essentially, my goal is to infer the generic argument of the component property and utilize it as the return type for the props function.

Answer №1

This topic can be a bit confusing when it comes to distributive conditional types. Essentially, what you need to do is enclose your condition in [] to make it actually true.

type ComponentConfig = never extends infer Props
  ? [Props] extends [Record<string, any>]
    ? {
        component: Component<Props>
        props(): Props
      }
    : never
  : never

If we analyze it further, never in a Union Type acts as if it doesn't exist at all.

type Result = string | number | never; // string | number
type Result2 = string | never; // string
type Result3 = never; // now it is never

When conditions result in never (empty union), it always returns another empty union which is also never. By enclosing never in array brackets ([]), we can retain that value in our union.

type Result4 = string | [never]; // string | [never];

However, we encounter difficulties with the typing of the component because the generic argument T of Component<T> ends up as never. To resolve this, pass the Props as a generic to your ComponenConfig<Props>. The use of infer here seems unnecessary.

type Component<Props extends Record<string, any>> = {
  props: Props;
};

type ComponentConfig<Props> = never extends Props
  ? [Props] extends [Record<string, any>]
    ? {
        component: Component<Props>;
        props(): Props;
      }
    : never
  : never;

const cfg: Record<string, ComponentConfig<{prop1: number}>> = {
  someKey: {
    component: {props: {prop1: 0}},
    props() {
      return {
        prop1: 1
      };
    }
   }
};

Playground Link

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

How can I retrieve a certain type of object property in TypeScript?

Imagine having a collection of flags stored in an object like the example below: type Flags = { flag1: string, flag2: string, flag3: boolean, flag4: number } // const myFlags: Flags = { // flag1: 'value 1', // flag2: 'value 1&ap ...

Having difficulty building a react.js application using Visual Studio 2019

Currently, I am following a helpful tutorial on creating a react.js application using visual studio. At this stage, the tutorial instructs me to open the command prompt and enter the following command: webpack app.tsx --config webpack-config.js (I have ...

The property 'item' is not found within the specified type 'IntrinsicAttributes & RefAttributes<Component<{}, any, any>>'. Error code: 2322

"react": "^16.12.0", "typescript": "^4.0.3", "next": "^9.4.4" The error being raised by typescript is related to the <Item item={item} key={item.id} urlReferer={urlReferer} /> prop used ...

Resolving the Challenge of Disabling typescript-eslint/typedef in Angular 13 with ESlint

I started a fresh project in Angular 13 and configured typescript-eslint by running the command below: ng add @angular-eslint/schematic I made changes to my .eslintrc.json file where I disabled the rules for "typescript-eslint/typedef" and "typescript-esl ...

Dealing with asynchronous operations in a pipeline with fp-ts

I'm currently exploring fp-ts and have been contemplating how to restructure my functions in order to steer clear of nested folds. While many online examples showcase a clean invocation of the pipe function, I am struggling to eliminate the nested fol ...

I'm in need of someone who can listen and detect any changes in my notifications table (node) in order to perform real-time data

Seeking a listener in Firebase to track changes in my notifications table for real-time data monitoring. My project is utilizing Angular 2 with TypeScript and Firebase. ...

Access a designated webpage with precision by utilizing Routes in Angular

Is it possible to display a different component in Angular routing based on a condition in the Routing file? For example, if mineType is equal to "mino", can I navigate to another component instead of the one declared in the Routing? Should I use Child ro ...

Derive the property type based on the type of another property in TypeScript

interface customFeatureType<Properties=any, State=any> { defaultState: State; properties: Properties; analyzeState: (properties: Properties, state: State) => any; } const customFeatureComponent: customFeatureType = { defaultState: { lastN ...

Rendering Information in Angular 4 Through Rest API

Encountering issues displaying data from my local express.js REST API, organized as follows: people: [{ surname: 'testsurname', name: 'testname', email: '<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemai ...

In TypeScript, the 'onChange' is declared multiple times, therefore this particular usage will be scrutinized carefully

Within my React project, I am utilizing material-ui, react-hook-form, and Typescript. However, I encountered an error in VSCode when attempting to add the onChange function to a TextField component: 'onChange' is specified more than once, resul ...

Struggling to integrate a JavaScript sdk with an Angular2 application due to missing dependencies

I've been struggling to incorporate the Magic: The Gathering SDK library into my Angular2 application. I've tried various methods, but nothing seems to work seamlessly. When I attempt to import the library using TypeScript like this: import { } ...

Understanding Vue.js - encountering the error message "property or method is not defined"

Recently, I've come across an issue that seems to be common among many people, but for some reason, I am unable to find a solution despite looking at similar questions. The problem arises when I use a v-for in a Vue Component and the array value cons ...

What are some ways to troubleshoot the TypeScript React demonstration application in Chrome?

Debugging a TypeScript app in the Chrome debugger is a straightforward process. First, you need to configure the tsconfig.json file: "sourceMap": true, Next, install ts-node and set a breakpoint in your TypeScript code using "debugger;" ...

What is the reason behind material-ui's decision to invoke their dialogs twice?

After attempting to implement a modal and realizing the strange behavior, I switched to using a dialog instead. To my surprise, the issue persisted. This is how I approached it: import Dialog, { DialogProps } from '@material-ui/core/Dialog'; imp ...

The promise chain from the ngbModal.open function is being bypassed

I'm currently working on implementing data editing within a component. My task involves checking if any of the data fields have been altered, and if so, prompting a confirmation pop-up to appear. If the user confirms the change, the data will then be ...

Transforming API response data into a Typescript object using React mapping

When I make an API call, the response data is structured like this: [ { "code": "AF", "name": "Afghanistan" }, { "code": "AX", "name": "Aland Islands" } ...

Import statement cannot be used except within a module

I am currently facing an issue with running the production version of my code. I have Node 20.10 and TypeScript 5 installed, but for some reason, I am unable to run the built version. Here are the contents of my package.json and tsconfig.json files: { & ...

What is causing me to not receive a 404 error when dealing with an unhandled state?

Currently, I am utilizing $stateProvider to configure my states in the following manner: constructor($stateProvider, $urlRouterProvider, $locationProvider) { $stateProvider. state("something", { url: "/index.html" }) ...

Error: 'next' is not defined in the beforeRouteUpdate method

@Component({ mixins: [template], components: { Sidebar } }) export default class AppContentLayout extends Vue { @Prop({default: 'AppContent'}) title: string; @Watch('$route') beforeRouteUpdateHandler (to: Object, fro ...

Error in Angular: Http Provider Not Found

NPM Version: 8.1.4 Encountered Issue: Error: Uncaught (in promise): Error: Error in ./SignupComponent class SignupComponent_Host - inline template:0:0 caused by: No provider for Http! Error: No provider for Http! The error message usually indicates the a ...