The expected data type was inferred as an object, but the function is returning an array

Need help with creating a type-safe function that can accept an argument representing a prop that either has a primitive value or a specific object with values of the same primitive type. Examples include t=number, t={x:number}, or t={x:number,y:number}.

For instance, consider a prop called grid which is initially of type number but can be further specified with fields like horizontal|vertical. Valid inputs should include grid=10, grid={horizontal:10}, grid={vertical:10}, or grid={horizontal:10,vertical:10}.

Here's a working implementation:

(implementation code here)

TypeScript playground link

Now I want to make the function more generic by allowing the specification of the horizontal and vertical fields as generics as well:

(updated implementation code here)

Updated TypeScript playground link

In simpler terms, why does the following return an array instead of an object?

// previous implementation that works fine
{
  horizontal: Prop extends Primitive ? Prop : Prop extends { horizontal: infer V } ? V : Default;
  vertical: Prop extends Primitive ? Prop : Prop extends { vertical: infer V } ? V : Default;
}

// updated implementation that returns an array - WHY?
{
  [key in keyof Spec]: Prop extends Primitive ? Prop : Prop extends { [key in keyof Spec]: infer V } ? V : Default;
}

To see complete examples and test the functionality, visit the provided TypeScript playground links. The JavaScript functions correctly but the inferred type seems to be incorrect. Any assistance would be greatly appreciated.

Answer №1

If I were to make some changes, the first adjustment would involve altering the return type of the function. Given that Spec represents a tuple, performing mapping operations on it will yield another tuple. It might be more beneficial to map over the entire contents within Spec.

{
  [key in Spec[number]]: Prop extends Primitive 
    ? Prop 
    : Prop extends { [key in keyof Spec]: infer V } 
      ? V 
      : Default;
}

Another issue concerns how Spec is inferred. The current specification of the argument fields causes Spec to always be detected as just a simple string[] instead of a tuple like

["horizontal","vertical"]
.

To rectify this situation, the spread syntax can be employed.

const parsePossiblySpecific = <
  Spec extends string[],
  Prop extends PossiblySpecific<Primitive, Spec[number]>,
  Default extends NonNullable<Primitive> | undefined = undefined
>(
  prop: Prop,
  fields: [...Spec],
  defaultValue?: Default
)

Playground

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

Why is webpack attempting to package up my testing files?

In my project, I have two main directories: "src" and "specs". The webpack configuration entrypoint is set to a file within the src directory. Additionally, the context of the webpack config is also set to the src directory. There is a postinstall hook in ...

Instance of an Angular TypeScript class

I have a TypeScript class named User with various properties such as id, userName, age, and more. The class also includes a fullName method that combines the givenname and surname of the user. Additionally, there is a sayHello function that logs a greeting ...

Challenges with Material UI and Styled Components Overriding

When utilizing Material UI's Typography, everything was functioning perfectly: <Typography component="h1" variant="h5"> Sign in </Typography> However, I decided to transition to styled-components and attempted the foll ...

Using TypeScript to implement a nested static class with enforced generic type constraints

As an illustration, let's consider a basic linked list class in TypeScript version 3.7.5. A LinkedList<T> is composed of a series of ListNode<T>, where the type variable T remains consistent between them. In this scenario, a private static ...

Utilizing Lazy Loading Modules within an Angular 2 (v5) App

I'm struggling to implement lazy loading in my Angular 2 (version 5.1.3) project. While following Todd Motto's guide on Lazy Loading Code Splitting, I am hitting a roadblock in getting it to function correctly. My app consists of multiple modul ...

Dependencies of generic types among function arguments

Exploring the implementation of binding in a game engine, I aim to incorporate a touch of typing. /** Engine external functions */ export type Message<TBody> = { } // This function returns the same unique object for the same `s` // An internal engi ...

What is the process for transforming a key-value object from JavaScript to TypeScript?

I am currently facing a challenge in converting a JavaScript object into a TypeScript version as part of our code refactoring process :( I am struggling to figure out how to properly migrate a JS JSON object into a correct TS format. For instance, consider ...

Storing information in an array based on a specific flag

Currently, I am developing an Angular application where I am dealing with a specific array that contains a flag named "checked". Based on the value of this flag, I need to perform certain manipulations. Here is a snippet of my sample data: const data = [{ ...

Combining the output of two Observables through the CombineLatest method to generate a

I possess two separate collections of information: Data Model: taxControlReference [ { "providerId": "HE", "taxTables": { "STAT": [ 1 ] } }, ...

Exploring the best practices for utilizing the error prop and CSS with the Input API in Material UI while utilizing context

When working with the MUI Input API props and CSS, I encountered an issue related to the {error} and its use. This is how my code looks: const [value, setValue] = useState<string>(cell.value); const [startAdornment, setStartAdornment] = useState< ...

Guide to encoding an array of objects into a URI-friendly query string using TypeScript

Just getting started with typescript and looking for some help. I have an input array structured like this: filter = [ { field : "eventId", value : "123" }, { field : "baseLocation", value : "singapore" } ] The desired format for ...

Getting News API and showcasing the information in Vuetify.js card components: A step-by-step guide

I'm trying to develop a news website by utilizing the News API for news data. I obtained an API Key from the official News API website, but my code is encountering some issues. The error message reads: TypeError: response.data.map is not a function ...

The functionality of GetStaticProps with Typescript is only operational when defined as an arrow function, rather than a function

The documentation for GetStaticProps in NextJs explains it as a function declaration. When trying to add types to it, the following code snippet results: export async function getStaticProps(): GetStaticProps { const db = await openDB(); const fa ...

Is it possible to change the value of a react-final-form Field component using the onSelect function?

I am currently working on a React application using TypeScript and incorporating the Google Places and Geocoder APIs through various React libraries such as "react-places-autocomplete": "^7.2.1" and "react-final-form": "^6.3.0". The issue I'm facing ...

Half the time, the Paypal button fails to load on Angular

My page has a recurring issue with a PayPal button that fails to load half the time. I recently experienced this problem when refreshing the page 30 times in a row – it alternated working properly for 9 times, then failed to load for 11 consecutive times ...

Can a concept like "A Rectangle can be a Square but a Square cannot be a Rectangle" be formulated?

Currently, I am working on developing a unique "loose" nominal type that allows assignment from its base type, but restricts assignment from other nominal types with the same base type. Is there a way to modify the existing Nominal type found here to achie ...

Using Angular to declare a variable for reuse within nested HTML elements

Exploring the realm of angular development has sparked my interest, however, I found myself at a roadblock while reading through the documentation. The issue lies in figuring out how to declare a variable that can be reused effectively within nested HTML e ...

Show the same values only once when using the ngFor directive in Angular, while the remaining values continue to loop

Here is a sample data set retrieved from the server: [ { subMenuId: 1, submenu: 'Users', menu: 'Administration', url: '/pms/admin/usrs-dsh', icon: 'fas fa-cogs', }, ...

System that sends out SMS alerts and Push Notifications

My Angular 7 application, which is almost complete, relies on Firebase services, including Cloud Firestore. I am seeking a solution to automatically send SMS or Push Notifications for appointment reminders without the user needing to be logged in. Is ther ...

Removing a portion of an item with the power of RxJS

I possess the subsequent entity: const myObject = { items:[ { name: 'John', age: 35, children: [ { child: 'Eric', age: 10, sex: 'M' }, { ...