Could this be a Typescript glitch? Getting the error "type not assignable" while trying to include a new property in an object

Dealing with two APIs that have contrasting property names for longitude coordinates—lon and lng, I am in the process of creating a converter function to accommodate both sets of data. This way, locations from either API can be used seamlessly.

Encountering an error while implementing this function, it strikes me as a potential bug. Despite being aware of various workarounds, I am curious to delve into the root cause of this issue. Is it truly a bug, or is there a piece of the puzzle that I may be overlooking?

const fillLonLng1 = <T extends LatLng | LatLon>(loc: T): T & LatLon & LatLng => {
    if ( isLatLon(loc) ) {
        return {...loc, lng: loc.lon};
    } else {
        return {...loc, lon: loc.lng};
    }
}

The error arises at the first return statement:

Type 'T & { lng: number; lat: number; lon: number; }' is not assignable to type 'T & LatLon & LatLng'.
Type 'T & { lng: number; lat: number; lon: number; }' is not assignable to type 'LatLon'.(2322)

Although TypeScript correctly recognizes that the returned value includes both lat and lon properties, each with a number value, the mismatch with the LatLon type leaves me puzzled. The definition of LatLon is as follows:

interface LatLon {
    lat: number;
    lon: number;
}
const isLatLon = <T extends Partial<LatLon>>(loc: T): loc is T & LatLon => {
    return loc.lat !== undefined && loc.lon !== undefined;
}

Complete Typescript Playground. Upon exploration, I discovered two alternative solutions that successfully bypass this error message without resorting to as. One involves splitting the function into separate parts, while the other employs intricate typings.

Answer №1

When dealing with a generic type parameter T extends A | B, encountering an error like "T & X is not assignable to T & Y" where X and Y are equivalent but not identical, it may be due to a compiler bug as discussed in microsoft/TypeScript#24688.

To resolve this issue, you can refactor the combination of LatLon & LatLng into a type that the compiler considers identical to

{ lng: number; lat: number; lon: number; }
:

interface LatLonLng extends LatLon, LatLng { }
const fillLonLng1 = <T extends LatLng | LatLon>(loc: T): T & LatLonLng => {
    if (isLatLon(loc)) {
        return { ...loc, lng: loc.lon };
    } else if (isLatLng(loc)) {
        return { ...loc, lon: (loc as LatLng).lng };
    } else throw new Error();
}

While some caveats remain, including rare edge cases like the one mentioned above, using a type assertion can help mitigate such issues:


As highlighted in this discussion, errors can arise when attempting to craft a value of a generic type because the caller of the function has control over specifying the generic type, potentially leading to unexpected outcomes.

For instance, a function with T extends LatLng | LatLon as its constraint can lead to surprising results, as demonstrated by scenarios involving property narrowing among other factors.

// Code examples illustrating potential pitfalls

Given these considerations, it's important to handle such scenarios carefully, possibly resorting to type assertions to ensure code safety.

Answer №2

Here is a solution you can try out:

interface Coordinates {
  latitude: number;
  longitude: number;
}
namespace Coordinates {
  export function isValidLocation(loc: object): loc is Coordinates {
    return ['latitude', 'longitude'].every(key => key in loc && loc[key] !== undefined);
  }
}

type Position = { lat: number; long: number; };

export function updateCoordinates(loc: Position | Coordinates): Coordinates & Position {
  return Coordinates.isValidLocation(loc) ? { ...loc, longitude: loc.long } : { ...loc, long: loc.longitude };
}

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

What is the best way to set up an endpoint in Angular for image uploading?

Using the Kolkov Angular editor in my Angular application, I have successfully created a rich text editor. Currently, I am looking to upload images from the editor to the server. I already have a function in place that takes a file as an argument and send ...

Multiple keyup events being triggered repeatedly

Currently, I am developing an Angular 4 application. Within my component's HTML, there is a textbox where users can input text. As soon as the user starts typing, I want to trigger an API call to retrieve some data. The current issue I am facing is t ...

Angular does not wait for the backend service call response in tap

Does anyone have a solution for subscribing to responses when the tap operator is used in a service? edit(status) { dataObj.val = status; // call post service with status.. this.service .update(dataObj) .pipe(takeUntil(this._n ...

Is there a way to correct Typescript's misunderstanding of the interface from a JSON file that has been imported?

The structure of my JSON file is as follows: [ { "keys": [ { "date": "2019-06-25T17:33:39.000Z" } ], "tag": null }, { "keys": [], "tag": "stringvalue" } ] Upon importing the file, Typescript assumes that ke ...

Is it possible for TypeScript to automatically detect when an argument has been validated?

Currently, I am still in the process of learning Typescript and Javascript so please bear with me if I overlook something. The issue at hand is as follows: When calling this.defined(email), VSCode does not recognize that an error may occur if 'email ...

What is the Typescript compiler utilized by Visual Studio 2015 when compiling on save?

Currently using Visual Studio 2015 Update 3 with TypeScript 2 for VS installed. I have a basic ASP.NET Core MVC web application with a few simple TypeScript files. The project contains a tsconfig.json file in the root folder with "compileOnSave": true. I ...

How to match and merge two arrays of objects based on a shared value in JavaScript

let array1 = [{ "id": "lap-143240121", "position": 0 }, { "id": "lap-15040293", "position": 1 }, { "id": "lp-1504444", "position": 2 }, { "id": "lp-150440987", "position": 3 }] let array2 = [{ "id": "lap-143240121", "name": "name1" }, ...

Specialized purpose for typed arrays

Given an array containing elements of two different entities: interface A { type: string } interface B { type: number } const a = {} as A const b = {} as B const array = [a, b] The array is of type (A | B)[] How can we create a utility type that ...

Error message: An unhandled TypeError occurs when attempting to access properties of an undefined object (specifically, the 'then' property) while refreshing the token using axios

Is there a way to refresh tokens in axios without interrupting the flow? For example, when the server returns an access token expiration error, I want to queue the request and replay it after getting a new token. In React, I'm using promises as shown ...

What is the reason behind the absence of forEach method on NodeListOf?

Here is the code that I wrote: var checkboxes = this.element.querySelectorAll("input[type=checkbox]") as NodeListOf<HTMLInputElement>; checkboxes.forEach(ele => { var key = ele.name; if (data.hasOwnProperty(key)) { ...

Ionic 2 struggles to manage a menu overlaying a Google Maps component

I have developed an Ionic V2 side menu application and incorporated a GMaps element into it. However, I am facing issues with handling elements that appear in front of the map. The buttons on the side menu become disabled when they are overlapped by the ma ...

Tips for managing local storage asynchronously

I have two files in my TypeScript application, namely: File 1 and File 2, In File 1, I want to save a value in local storage like this: private load() { return this.entityService .load(this.$scope.projectRevisionUid) ...

The resolution of Ionic 2 / Angular 2 providers is causing difficulty

Having issues with providers and injections in Ionic 2 since version 2.0.0. Encountering the following error (with various providers, not just this one): Uncaught Error: Can't resolve all parameters for LoginRedirectService: (App, ?, AlertContro ...

The SupabaseAuthClient type does not have a property named 'session' available

Here is the complete code snippet for page.tsx: "use client"; import React, { useState, useEffect } from "react"; import axios from "axios"; import { Session } from "@supabase/supabase-js"; import { Auth } from " ...

The React Table is showing an error due to incompatible property types for 'accessor'

Currently experimenting with react-table in a create-react-app project (version ^7.0.25). Utilizing the example provided in their quick start documentation. However, encountered a type error between the accessor and data columns. Below is the snippet of co ...

Tips for integrating TypeScript files into Next.js HTML files

Encountering an issue while trying to load a typescript file from HTML, resulting in this error Here is the code snippet for the page: export default function Home() { return ( <> <Script src="/static/main.tsx"></Scri ...

Swap out each addition symbol with a blank space within a given text

I'm currently working on a Typescript project where I need to convert URL parameters into a JSON object. The issue I'm facing is that some values are concatenated with a '+'. How can I replace this symbol with a space? Here's the ...

Access the child component within an @ChildComponent directive in Angular

Is it possible to retrieve child components of another component? For instance, consider the following QueryList: @ContentChildren(SysColumn) syscolumns: QueryList<SysColumn>; This QueryList will contain all instances of the SysColumns class, which ...

Upon receiving data from the Api, the data cannot be assigned to the appropriate datatype within the Angular Object

I am encountering an issue with the normal input fields on my page: https://i.stack.imgur.com/qigTr.png Whenever I click on the "+" button, it triggers an action which in turn calls a service class with simple JSON data. My intention is to set selectionC ...

Ways to Obtain Device Identification in Ionic 2 Utilizing TypeScript

Can anyone help me with getting the device ID in ionic2 using typescript? I have already installed the cordova-plugin-device Here is my code snippet: platform.ready().then(() => { console.log(device.cordova); } Unfortunately, this code doesn&apos ...