Losing the generic type in nested interfaces

Having issues with generics and 'nested' interfaces when working with ReadonlyArray. Is there a way to utilize the filterList function without losing the type for the list parameter?

interface INumber {
    value: number
}

interface IState {
    readonly numbers: ReadonlyArray<INumber>
}

var state: IState = {
    numbers: [{ value: 1 }, { value: 2 }, { value: 3 }, { value: 4 }]
} 

function filterList<T>(list: T) {
    return list.filter(x => x.value > 2);
}

// Error: Property 'filter' does not exist on type 'T'.
const res = filterList(state.numbers);

If we attempt to modify the filterList function as shown below, it will fail due to the use of ReadonlyArray.

function filterList(list: INumber[]) {...}

// Error: Argument of type 'ReadonlyArray<INumber>' is not assignable
// to parameter of type 'INumber[]'. Property 'pop' is missing in 
// type 'ReadonlyArray<INumber>'.
const res = filterList(state.numbers);

How should this situation be best handled? Appreciate any insights!

Answer №1

There are various options available to you. ReadonlyArray<T> is a subset of Array, allowing us to constrain T to a ReadonlyArray containing values:

var state: IState = {
  numbers: [{ value: 1 }, { value: 2 }, { value: 3 }, { value: 4 }]
} 

function filterList<T extends ReadonlyArray<{value : number }>>(list: T){
  return list.filter(x => x.value > 2);
}

const res = filterList(state.numbers); // successful

var arr = [{ value: 1 }, { value: 2 }, { value: 3 }, { value: 4 }]
const res2 = filterList(arr); //successful

Although specifying the type parameter as the list doesn't offer much advantage, as filter will always return a regular array regardless of whether it's a ReadonlyArray or Array. Hence, this alternative approach will also function correctly:

function filterList<T extends {value : number }>(list: ReadonlyArray<T>){
  return list.filter(x => x.value > 2);
}
const res = filterList(state.numbers); // successful

var arr = [{ value: 1 }, { value: 2 }, { value: 3 }, { value: 4 }]
const res2 = filterList(arr); //successful

If you wish for filterList to retain the information on whether a read-only array or a regular array was passed, you can opt for the initial version and return T. However, in order to ensure that the result of filter is typed as a ReadonlyArray, a type assertion may be necessary:

function filterList<T extends ReadonlyArray<{value : number }>>(list: T): T{
  return list.filter(x => x.value > 2) as any;
}

const res = filterList(state.numbers); // successful, res is ReadonlyArray<INumber>

var arr = [{ value: 1 }, { value: 2 }, { value: 3 }, { value: 4 }]
const res2 = filterList(arr); //successful, res is { value: number; }[]

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

Tips on Identifying the Category

I am currently studying TypeScript. Recently, I have been using Axios to fetch API data, and then I stored the returned value in a useEffect hook. However, when trying to display it on the screen, I encountered an error stating that there is no 'name ...

Issue with InversifyJS @multiInject: receiving an error stating "ServiceIdentifier has an ambiguous match"

Having an issue with inversifyJs while trying to implement dependency injection in my TypeScript project. Specifically, when using the @multiInject decorator, I keep receiving the error "Ambiguous match found for serviceIdentifier". I've been referenc ...

Ways to generate an Angular 7 component

Seeking guidance on creating an angular 7 component. I have forked a jsFiddle at this link: https://jsfiddle.net/gauravshrestha/fdxsywLv/. The chart in the fiddle allows data points to be dragged up and down. My goal is to convert this into a component whe ...

"When attempting to render a Node inside the render() method in React, the error message 'Objects are not valid as a React child' is

On my webpage, I have managed to display the following: export class OverworldComponent extends React.Component<OverworldComponentProps, {}> { render() { return <b>Hello, world!</b> } } However, instead of showing Hello, ...

Should I opt for the spread operator [...] or Array.from in Typescript?

After exploring TypeScript, I encountered an issue while creating a shorthand for querySelectorAll() export function selectAll(DOMElement: string, parent = document): Array<HTMLElement> | null { return [...parent.querySelectorAll(DOMElement)]; } ...

Error message: Property is not found in the $rootScope object in AngularJS

Encountering an issue while attempting to assign a value to the rootscope in Typescript. class TestClass{ this.rootScope: ng.IRootScopeService; constructor($rootScope){ this.rootScope = $rootScope; } addValueToRoot=()=>{ ...

unable to transform this string into an object

https://i.sstatic.net/O46IL.pngWhy am I encountering difficulties converting this string into an object? Any assistance on resolving this error would be greatly appreciated. onSignup(data:any){ localStorage.setItem('users',JSON.string ...

Typescript: Creating a new type by filtering an interface for matching properties

Imagine you have an interface structured like this: interface endpoints { "/api/user/{id}": { get: operations["getUserGET"]; }; "/api/user/add": { put: operations["addUsingPUT"]; }; ... } Is it pos ...

You are unable to compile a module in Visual Studio Code unless you provide the --module flag

I am facing an issue with my TypeScript code file that appears to be a common error, but I'm struggling to resolve it. This problem is new to me as I am still getting acquainted with Visual Studio Code. Cannot compile modules unless the '--modul ...

What is the best way to assign a TypeScript type as the object type within an array of objects sourced from a different interface?

I am working with a generated interface that looks like this: export interface StaticPageLeftMenuV1 { id: string status: 'draft' | 'published' environments: ('dev' | 'staging' | 'production')[] ...

What is the method for choosing an element by class name in TypeScript?

Currently, I'm working on creating a responsive menu bar that collapses on smaller screens. The challenge I'm facing is that I'm using TypeScript for this project. Is there any guidance on how to translate the following code into TypeScript? ...

What is the best way to implement multiple ternary operators within HTML code?

Consider the following code snippet: It currently applies CSS classes up to red4, but I want to apply CSS classes up to red11. Additionally, the variable "size" in myData should be dynamic. For example, size could range from 0-20 // 0-100 // 0-10000, etc., ...

Utilizing React TypeScript: Leveraging useRef for Linking purposes

Implementing useRef to Handle Link Clicks import {Link} from 'react-router-dom'; const myLinkRef = useRef<HTMLAnchorElement>(null); ... myLinkRef.current.click() ... <Link to={{pathname: '/terms'}} id='myLink' ref= ...

Ensure that the interface limits the key value to match precisely the value of a constant in Typescript

Seeking assistance in understanding how to enforce a specific type for an optional key within an interface: const FIRST = "FIRST" const SECOND = "SECOND" interface TSomeInterface { element: Element order?: typeof FIRST | typeof ...

React component stuck in endless loop due to Intersection Observer

My goal is to track the visibility of 3 elements and update state each time one of them becomes visible. Despite trying various methods like other libraries, useMemo, useCallback, refs, etc., I still face challenges with my latest code: Endless loop scenar ...

"Efficiently Distributing HTTP Requests Among Simultaneous Observers using RxJS

Currently, I am working on a feature in my Angular application that requires handling multiple concurrent fetches for the same data with only one HTTP request being made. This request should be shared among all the subscribers who are requesting the data s ...

Embedding Dropzone in Angular 2 or Typescript is already implemented

Within my Angular 2 Component, I have a Dropzone that is created programmatically and I want it to be attached to the body so that my entire website can serve as the "dropzone" for file uploads. Every time the component is initialized, it attempts to atta ...

How come TypeScript remains silent when it comes to interface violations caused by Object.create?

type Foo = { x: number; }; function g(): Foo { return {}; // Fails type-check // Property 'x' is missing in type '{}' but required in type 'Foo'. } function f(): Foo { return Object.create({}); // Passes! } functio ...

If placed in the same document, will promises be executed sequentially?

Let's say I have a function in one file that returns a promise: public async a():Promise<string>{ return 'hi' } In another file, I use this function like so: await service.a.then( hi =>console.log(hi)).catch(err=>{throw err}); ...

The sanitizer variable becomes null when accessed outside of the NgOnInit function in Angular using TypeScript

At first, I added DomSanitizer to the component: import { DomSanitizer, SafeResourceUrl} from '@angular/platform-browser'; Next, a class was created and included in the constructor: export class BlocklyComponent implements OnInit { primar ...