What could be causing TypeScript to display errors in unexpected locations while inferring inner types?

I encountered a rather intricate issue that's challenging to sum up in a brief title, my apologies for that. I devised a function that accepts a generic params type and returns a result type constructed from the params type. It utilizes string literals to describe the properties on the resulting type. The intention was to enable the IDE to offer suggestions and autocomplete when creating the params type (variable), and this aspect works as expected. However, I've now realized that typos in the non-generic properties of the params type no longer trigger detection by typescript. Instead, strange errors are reported regarding the correct usage of the generic parts. It seems like the inference engine is behaving oddly.

The initial aim behind the complex generic type was to enhance user experience, but with simple typos in standard properties leading to confusing error messages, that benefit has dissipated.

Can anyone shed light on the reason for this error or provide a solution?

export type Variable = "temperature" | "rain" | "wind_speed" export interface Params<V extends Variable> { latitude: number longitude: number variables?: V[] } export interface Data<V extends Variable> { timestamp: number variables: [V] extends [never] ? never : Record<V, number> } export async function fetchData<V extends Variable = never>(params: Params<V>) { return {} as unknown as Data<V> // stub implementation }

Currently, an error is reported on 'rain' stating 'string not assignable to never', instead of highlighting the typo where longitud should actually be spelled longitude.

const example3 = await fetchData({
  latitude: 49.1,
  longitud: 11.5, // typo here, should be 'longitude'
  variables: ['rain'] // however, typescript flags the error at 'rain'
})

Answer №1

Whenever you make a call

const example3 = await fetchData({
  latitude: 49.1,
  longitud: 11.5, 
  variables: ['rain'] // error
})

The issue arises because the input object is of the wrong type, specifically due to the absence of the longitude property. Consequently, the compiler struggles to deduce the V generic type argument and defaults to the default never type. This leads the compiler to validate against Params<never>. The input fails to match as a Params<never> in two ways - missing longitude and incorrect typing of variables (not never[]). It's worth noting that the absence of the longitud property isn't really a type error, but rather an excess property check which might not be the primary concern. Excess properties are generally allowed; refer to extend and only specify known properties? for more insights.

Hence, your input faces two challenges with the compiler, leading it to prioritize flagging the variables property. While this might not align with your expectations, it falls within the compiler's discretion on how to highlight errors.


This explains the reasoning behind the issue. To resolve it, I recommend making the fetchData() function generic in the P type of the params input and utilizing that P to compute V. This approach prevents any unexpected never inference until after inferring the type of params. Here's how you can implement it:

export async function fetchData<P extends Params<Variable>>(params: P) {
  type V = P extends {variables: (infer V extends Variable)[]} ? V : never
  return {} as unknown as Data<V>
}

I've used a locally defined function-specific type alias for V, but you can inline it as well. Notice how V defaults to never if variables is omitted from P. While P impacts V, the reverse isn't true; irrespective of V's outcome, P remains unaffected, ensuring no unexpected fallback behavior anticipating never[] for variables.


Lets put it to the test:

const example1 = await fetchData({
  latitude: 49.1,
  longitude: 11.5,
  variables: ["rain"]
})
console.log(example1.variables.rain) // valid
console.log(example1.variables.temperature) // error

const example2 = await fetchData({
  latitude: 49.1,
  longitude: 11.5
})
console.log(example2.variables.rain) // error

const example3 = await fetchData({
  latitude: 49.1,
  longitud: 11.5, // error, surplus property
  variables: ['rain']
})

Seems like the expected behavior has been achieved.

Playground link to code

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

Dynamic TypeScript property that can only be assigned values from an array during runtime

I'm currently struggling with specifying allowed values for a property in TypeScript. Within my interface, I have statically typed the property like this: interface SomeInterface{ prop: "bell" | "edit" | "log-out" } However, I am looking for a w ...

The world of TypeScript generics and derived data types

I'm looking to streamline the process of calling multiple functions by creating a function that handles this task. These functions all require similar business logic and cleanup operations. function foo(arg: number) { // perform actions using arg ...

Transforming every piece of TypeScript code into JavaScript on the server-side

I'm relatively new to developing Node.js applications for production. I've created an app using TypeScript for the backend of my Node.js project. Currently, in the package.json file, I have the following script section: "scripts": { ...

Is there a way to assess Python code within a document's context using JavaScript in JupyterLab?

When using Jupyter Notebooks, I can create a cell with the following JavaScript code: %%javascript IPython.notebook.kernel.execute('x = 42') After executing this code, in another cell containing Python code, the variable x will be bound to 42 a ...

Guide on making a Mapped Type in TypeScript using dynamic generic parameters

I am working with an object that contains one or more PreparedStatement. I am looking to create a general type definition for this object. Is there a way to achieve this in code? type PreparedRequest<Input, Result> = { input: Input; result: Resul ...

The resend email feature isn't functioning properly on the production environment with next js, however, it works seamlessly in the development environment

import { EmailTemplate } from "@/components/email-template"; import { Resend } from "resend"; const resend = new Resend("myApiKey"); // this works only in dev // const resend = new Resend(process.env.NEXT_PUBLIC_RESEND_API_KE ...

Updating a one-to-one relationship in TypeORM with Node.js and TypeScript can be achieved by following these steps

I am working with two entities, one is called Filter and the other is Dataset. They have a one-to-one relationship. I need help in updating the Filter entity based on Dataset using Repository with promises. The code is written in a file named node.ts. Th ...

Uh oh, it looks like there's an issue! The function getCoordinates is not recognized for this.locations

Whenever I attempt to execute the method in my class (getCoordinates()), an Error is thrown: ERROR TypeError: this.locations[0].getCoordinates is not a function What am I doing wrong? The service method I'm using: getLocations(){ return this ...

What could be causing the TypeScript exhaustive switch check to malfunction?

How come the default case in the switch statement below does not result in an exhaustive check where 'item' is correctly identified as of type 'never'? enum Type { First, Second } interface ObjectWithType { type: Type; } c ...

Incorporating fresh components and newly defined attributes in Angular

Is there a way for me to click on a new component button, specify a name, description, select type, and add attributes such as default value and type? I need all this information to be saved and for the new button to appear in the drag-and-drop section. ...

Traveling from one child route to another

I am encountering issues with route navigation between child routes, as I keep receiving "routes not found" errors. Despite trying multiple solutions like injecting and refreshing after child configuration, I still face difficulties in navigating to a spec ...

The error message for Angular FormControl's minimum length validation is not showing up in the errors

My goal is to access the minlength error, but when I check all the errors, it's not there. Below is my form control title: new FormControl("", [Validators.minLength(10), Validators.required]), I expect to see both the required and minlengt ...

Tips for removing the download prompt in Firefox when using Selenium in Typescript

While attempting to download a file in Firefox, a download dialog box pops up. I am looking to disable the download dialog box specifically for zip files so that they are downloaded automatically. Despite trying various preferences settings, the dialog box ...

What is the best way to send props to a child component in JSX with TypeScript?

While passing props to the child, I encountered an error message stating "Property 'isClicked' does not exist on type 'IntrinsicAttributes & IntrinsicClassAttributes". I defined "isClicked?: boolean" in my code. What additional steps sho ...

Incorporate the Get Your Guide Widget into an Angular2 component

Currently, I am attempting to embed a "Get Your Guide" Widget within an Angular2 application. Although the script in index.html is being requested successfully, I am facing difficulties adding it to the HTML of the component. <script async defer src=" ...

"Unfortunately, Azure Web Static Apps do not have the capability to access .env files that are prefixed with NEXT

Suppose I have an .env file set up like this: NEXT_PUBLIC_API_BASE_PATH = value1 While this .env is functioning correctly in my local environment, once deployed to Azure Web Static Apps and added to the configurationhttps://i.sstatic.net/eqiYn.png My app ...

What is the most effective method to create a versatile function in Angular that can modify the values of numerous ngModel bindings simultaneously?

After working with Angular for a few weeks, I came across a problem that has me stumped. On a page, I have a list of about 100 button inputs, each representing a different value in my database. I've linked these inputs to models as shown in this snipp ...

What is the best way to retrieve the current height in VueJS using the Composition API?

I am utilizing a Ref to preserve the current height of the active element. My goal now is to transfer this height to the subsequent element that gets clicked on. <script lang="ts" setup> import { ref, reactive } from "vue"; defin ...

Sending a specific object and its corresponding key as parameters to a reusable component in Angular: T[K]

I am currently working on developing a generic component in Angular and Typescript version 4.4.4. The goal is to create a component where I can set both an object (obj) and specific keys (properties). Here's a simplified version of my text component: ...

What steps can I take to resolve the 'Object may be null' error in TypeScript?

I am facing a similar issue to the one discussed in this thread, where I am using Draft.js with React and Typescript. After following the code example provided in their documentation, I encountered the 'Object is possibly 'null'' error ...