Error encountered while utilizing the Extract function to refine a union

I am currently working on refining the return type of my

EthereumViewModel.getCoinWithBalance
method by utilizing the Extract utility type to extract a portion of my FlatAssetWithBalance union based on the generic type C defined in EthereumViewModel (which is restricted to 'eth' | 'polygon'). However, I'm encountering an issue where these types do not align with the types specified in my getAsset function, resulting in an error at the return statement within the getCoinWithBalance function.

How can I adjust either my getCoinWithBalance or getAsset functions to ensure that the types match correctly? Could you also provide some insights into why they are currently mismatched?

You can find this example on the TypeScript Playground here, and I have included the code snippet and error message below as well.

type ExpandRecursively<T> = T extends object
  ? T extends infer O ? { [K in keyof O]: ExpandRecursively<O[K]> } : never
  : T;
  
type ChainId = "eth" | "btc" | "polygon";
type EthereumChainId = Exclude<ChainId, "btc">;

type BlockchainInfo = {
    chainId: "btc";
} | {
    chainId: "eth";
    contract: string;
} | {
    chainId: "polygon";
    contract: string;
}

type ExtractChain<A extends BlockchainInfo, C extends ChainId> = Extract<A, { chainId: C }>;

type BaseAsset = {
    name: string;
    symbol: string;
    id?: number | undefined;
    image?: string | undefined;
    rank?: number | undefined;
    decimals: number;
    amount: number;
}

type FlatAssetWithBalance = BaseAsset & {
    chainId: "btc";
} | BaseAsset & {
    contract: string;
    chainId: "eth";
} | BaseAsset & {
    contract: string;
    chainId: "polygon";
}

type EthChainFlatAssetWithBalance<C extends EthereumChainId> = ExtractChain<FlatAssetWithBalance, C>;
type EthChainFlatAssetWithBalanceTest = EthChainFlatAssetWithBalance<'polygon'>; 

declare const getAsset: <C extends EthereumChainId>(chainId: C) => BaseAsset & { chainId: C; contract: string; };

class EthereumViewModel<C extends EthereumChainId> {
    chainId: C = 'eth' as C; // Just for the sake of testing

    getCoinWithBalance(): EthChainFlatAssetWithBalance<C> {
        // the below line is where the type error displays
        return getAsset(this.chainId);
    }
}

Below is the complete error message generated from the getCoinWithBalance return statement.

Type 'BaseAsset & { chainId: C; contract: string; }' is not assignable to type 'EthChainFlatAssetWithBalance<C>'.
  Type 'BaseAsset & { chainId: C; contract: string; }' is not assignable to type 'Extract<BaseAsset & { contract: string; chainId: "polygon"; }, { chainId: C; }>'.

Answer №1

The problem lies in the fact that your generic type C, while restricted to EthereumChainId type ('eth' | 'polygon'), can potentially encompass both types rather than just one from the union. This inconsistency affects the return type of getCoinWithBalance as the return type of getAsset never returns a union, yet the Extract type (utilized by getCoinWithBalance) could return a union of

BaseAsset & { contract: string; chainId: "eth"; } | BaseAsset & { contract: string; chainId: "polygon"; }
due to the possibility of including both chain IDs in C.

To resolve this issue, you simply need to adjust the return type of getAsset to align with the same Extract utility type like so:

declare const getAsset: <C extends EthereumChainId>(chainId: C) => ExtractChain<FlatAssetWithBalance, C>;

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

Firestore data displaying as null values

Recently, I encountered CORS errors while polling the weather every 30 seconds in my program. Upon investigating, I discovered that the city and country were being interpreted as undefined. To fetch user data from my users' table, I utilize an Axios ...

Having trouble installing memlab using the npm package

Recently, I made an attempt to install the memlab library from Meta's GitHub. Initially, when I installed it without using the -g flag, the installation was successful. However, I encountered an issue where I could not execute any of the memlab comman ...

Steps for running a TypeScript project as a child process within a JavaScript project

I am facing an issue with integrating my Electron app, written mainly in JavaScript, with an Express server project built in TypeScript. When I attempt to create a child process of the TypeScript project within my electron.js file, I encounter TypeScript e ...

Combining the values of a particular key with duplicate objects into a single object within an array of JSON objects using Angular and Typescript

I'm currently facing a challenge in my Angular project where I have an array of JSON objects. These objects are very similar, differing only in one key-value pair. My goal is to combine these similar objects into one while appending the varying values ...

What is the best way to access buffer data in TypeScript for Solana?

Is there a way to retrieve buffer data from TypeScript? I am attempting to use the public key to access all of my token lists, but I am only getting back an empty array of objects. import {Connection, Keypair} from "@solana/web3.js"; const Sola ...

Showcasing diverse content with an Angular Dropdown Menu

I'm currently developing an angular application, and I've encountered a difficulty in displaying the user's selection from a dropdown menu. To elaborate, when a user selects a state like Texas, I want to show information such as the period, ...

Using React to make an API call without utilizing hooks

Hello, I am currently working on developing a webpart using SharePoint and React. However, I am facing some issues with fetching data from a simple API. export default class Testing100 extends React.Component<ITesting100Props, {}> { constructor(p ...

"Ensuring the right data type is selected for the onChange event

In my code, I have a simple select component set up like this. import { Controller } from "react-hook-form"; import Select, { StylesConfig } from "react-select"; //.. const [universe, setUniverse] = useState<SetStateAction<TOptio ...

Switching out a traditional class component with a functional component within a hook to deduce properties from T

One challenge is to subtract props from T within the withHookFn function using a function instead of a class as successfully done in the withHook function. The code contains comments explaining this issue. Dive into the code for more insights. import Reac ...

The 'type' property within the NGRX Effect is not present in the type Observable<any[]>

I am currently in the process of upgrading my Angular app from version 6 to version 7. Additionally, I am upgrading the TypeScript version from 2.7.2 to 3.1.6. The issue I'm encountering is that TypeScript is flagging an error stating that my ngrx ef ...

Arrange a JavaScript map based on its values and ensure that a specific field index remains at the top position

I'm sure this question may seem simple to some, but as a JavaScript novice, I couldn't find the answer myself. Here is the code snippet I'm working with: Let map = new Map<String,String> map.set('0', select) map.set('1&a ...

What is the best way to retrieve an object within a class?

Exploring a Class Structure: export class LayerEditor { public layerManager: LayerManager; public commandManager: CommandManager; public tools: EditorTools; constructor() { this.commandManager = new CommandManager(); this.lay ...

Ways to implement material-ui button design on an HTML-native button

I am using pure-react-carousel which provides me an unstyled HTML button (ButtonBack). I would like to customize its style using material-ui. Trying to nest buttons within buttons is considered not allowed. An approach that works is manually assigning th ...

Determining the data type of a generic variable within an Angular component

I'm currently in the process of developing a versatile component that can handle data of only two specific types: interface X{ name: string, path: string, type: string, } interface Y{ name: string, path: string, } Both types X a ...

Simplify a function by lowering its cyclomatic complexity

This particular function is designed to determine whether a specific cell on a scrabble board qualifies as a double letter bonus spot. With a cyclomatic complexity of 23, it exceeds the recommended threshold of 20. Despite this, I am unsure of an alterna ...

Create a custom hook that encapsulates the useQuery function from tRPC and provides accurate TypeScript typings

I have integrated tRPC into a project that already has API calls, and I am looking to create a separate wrapper for the useQuery function. However, I am facing challenges in getting the TypeScript types right for this customization. My Objective This is w ...

What is the method for determining the data type of a column in an Excel sheet that has been

Currently, I am utilizing the XLSX npm library to convert an Excel sheet into JSON format. However, all of the retrieved data is currently being returned as strings. To see a demo of the XLSX read process, you can visit this Stackblitz demo Is there a w ...

Why does the pound symbol in z-index always show up in Angular?

Having an issue with my code where I set a z-index in the CSS like this: .mat-mini-fab { position: absolute; right: 5px; top: 4px; z-index: 999; box-shadow: none !important; } However, whenever I visit my site, the z-index is not being appl ...

Utilizing Array.every to refine a union of array types, narrowing down the options

I need to narrow down the type of a variable that is a union of different array types in order to run specific code for each type. I attempted to use Array.every along with a custom type guard, but encountered an error in TypeScript stating "This expressio ...

Dynamic autocomplete in Oclif utilizing an HTTP request

Is it feasible for Oclif to support the functionality of making API calls to retrieve values for autocomplete? Consider this scenario: A database stores multiple users information Upon typing show users <Tab> <Tab>, the CLI triggers an API ca ...