Collection of assorted objects with varying sizes that are all subclasses of a common superclass

I need to create an array that can hold any number of items, all of which are subclasses of the same base class. The issue I'm facing is with type checking in TypeScript - when I declare the array as Array<BaseClass>, I have to cast each item to its specific subclass for accurate type checking. This defeats the purpose of class polymorphism.

To illustrate:

class Base {
  name: string
}
class Sub1 extends Base {
  prop1: number
}
class Sub2 extends Base {
  prop2:string
}
// there will be other Base subclasses - unknown quantity, possibly from third party plugins

// public function called by end users - I want precise type checking on object literals without manual casting:

function f(arr: Base[]){
}

// TypeScript incorrectly assumes every item is Sub1, even if explicitly defining one as Sub2:
f([
  {name: '1', prop1: 1} as Sub1,
  {name: '1', prop2: '2'} as Sub2,
])

// Requires casting but performs proper typechecking:
f([
  {name: '1', prop1: 1},
  {name: '1', prop2: '2'},
] as [Sub1, Sub2])

When calling overloaded methods or functions in similar situations, TypeScript matches signatures without needing casts. Is this avoidance intentional due to potential performance issues with large arrays?

For users to call f() elegantly without forced casting, I am seeking a way to achieve exact type checking without manual casting. In an object-oriented language, such as Java, instances of subclasses should not require casting since they are still instances of the base class.

Note: Unable to use a type union (arr: Array<Sub1|Sub2>) as I do not know all possible subclasses provided by third parties.

In languages like Java, you would declare the array as Array<? extends Base>. Perhaps TypeScript has a similar feature that I am unaware of.

Thank you!

Answer №1

If you encounter an error, it is likely triggered to catch typos, especially in mis-spelled property names during object initialization.

However, if you have an existing array, the function will accept it as long as it contains a name property.

var arr = [
    { name: '1', prop1: 1 },
    { name: '1', prop2: '2' }
];

f(arr);

Exploring Additional Scenarios

In cases where items are created dynamically, you can inform the compiler to expect various properties:

class Base {
    name: string;
    [key: string]: any;
}

This provides transparency to the compiler that...

A name is certain to exist... while other properties may be present too.

Intersection Type Insight

To avoid modifying your Base class, utilize a trick by forming an intersection type using both Base and a separate Indexed type.

class Base {
    name: string;
}

type Indexed = { [key: string]: any; }

// ...

function f(...arr: (Base & Indexed)[]) {
}

// ...

Assigning a Descriptive Name

To enhance organization, consider assigning a name to the intersection type. This practice proves useful when utilizing the type across multiple instances.

// ...

type SubOfBase = Base & Indexed;

function f(...arr: SubOfBase[]) {
}

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

Creating a dynamic array in an Angular 2 service for real-time changes monitoring by a component

I am facing an issue where my NotificationsBellComponent is not receiving changes to the array in the service even though the _LocalStorageService is returning data correctly. Q) How can I ensure that my component receives updates when the service collect ...

Step-by-step guide on filtering an array of objects using Vuejs and TypeScript

For this particular project, I am utilizing Vuejs Typescript with a data structure that looks like this: ["order": { "id":1, "created_at":"2019-12-06T10:22:17Z", "status":"open", ...

Global Enum in Typescript that is Optimized for Inlining during Compilation

I'm facing a challenge with an enum that is widely used in my project. Having to import it into every file is becoming cumbersome. Is there a way to define the enum in the .d.ts file so that it automatically gets included when compiled to Javascript? ...

Update the mandatory fields in the required interface to extend to another interface, while ensuring that all subfields become

Currently, I have defined 2 interfaces: interface BattleSkills { strength: number; armor: number; magic_resistance: number; health: number; mana: number; intelligence: number; accuracy: number; agility: number; critical_damage: number; } ...

Is it possible to retrieve and utilize multiple Enum values in Typescript?

Combine enum values to retrieve corresponding enum strings. Consider the following scenario: enum EnumDays { NONE = 0, SUN = 1, MON = 2, TUE = 4, WED = 8, THU = 16, FRI = 32, SAT = 64, ALL = 127 } If I pass a value o ...

Is it advisable to specify data types for my JSON data in TypeScript?

For the shopping application in my project, I am utilizing a JSON structure to categorize products as either hot or branded. However, I encountered an issue when trying to define a type for the entire JSON object labeled "full". Despite my attempts, it app ...

Creating a subtype in typescript involves specifying values for certain fields and getting rid of any optional members in the type definition

interface Person{ name:string, age:number, gender?:string } interface Employee extends Person{ name='John Doe', age:number } I am trying to achieve the above structure for a person and employee in TypeScript. I am also curious if something simi ...

Utilize SCSS values within TypeScript by applying them on a class level

let changeClassDisplay = document.getElementsByClassName('sidebar'); for (var i = 0; i < changeClassDisplay.length; i += 1) { changeClassDisplay[i].style.display = 'block'; } I encountered an issue with this code whe ...

What is the best approach to incorporate Column Reordering in react-data-grid?

Within my REACT application, I have incorporated the npm package react-data-grid. They offer a sample showcasing Column Reordering on their website. I wish to replicate this functionality in my own code. Upon reviewing their source code, I am puzzled abou ...

Exploring the functionality of CanDeactiveGuard and ModalDialogService through unit testing

In my application, the CanDeactiveGuard is functioning properly. During unit testing, I encountered an issue with one test where I intended to use callThrough to invoke the openConfirmDialog() method within the Guard. This method triggers the Modal Dialog ...

Here is the revised text: "What is the best way to send a variable to a component and retrieve it within the ng-template using

I've developed a component named "foo" that accepts a data object and displays it within an ng-template as its own variable. The issue arises when the variable inside the ng-template is of type any, lacking proper typing for checking and autocomplete ...

Having trouble reading properties of undefined (specifically 'listen') during testing with Jest, Supertest, Express, Typescript

Issue: While running jest and supertest, I encounter an error before it even gets to the tests I have defined. The server works fine when using the start script, and the app is clearly defined. However, when running the test script, the app becomes undefi ...

Encountering challenges while integrating Angular with a Laravel forum creation project

Currently, I am working on building a forum application that involves users, posts, and comments using Laravel. However, the next step in my project requires integrating Angular, which is new territory for me and I'm not sure where to start. I have a ...

Error: The function 'some' is not recognized in the rawData variable in REACT/ANTDESIGN

I've been grappling with this issue for nearly a full day now. Despite exhausting all possible solutions and conducting extensive searches, I'm still stumped. My task is to create a table using ant design where all the users are displayed upon i ...

New approach in Typescript: Enhancement of child class with additional Event Listener overloads

One of my classes is structured like this: interface A extends EventEmitter{ on(event: "eventA", listener: () => void): this; } There is another class defined as follows: interface B extends A{ on(event: "eventB", listener: ...

Encountering an issue when trying to upload numerous base64 images to Cloudinary through Node.js and receiving an error with code -4064, specifically 'ENAMETOOLONG'

I'm encountering an issue with base64 URLs when trying to upload multiple images to Cloudinary. When I send only one image, it gets uploaded correctly, but when sending multiple images, I receive an error 'ENAMETOOLONG' with error number 406 ...

Angular: ngx-responsive has a tendency to hide elements even if they meet the specified conditions

Recently, I started using a library called this to implement various designs for desktop and mobile versions of an Angular app (v4.2.4). Although the documentation recommends ngx-responsive, I opted for ng2-responsive but encountered issues. Even after set ...

Encountering error TS2304: Cannot resolve name 'classes' when attempting to apply styling in React using Typescript

I'm having trouble customizing the styles on my website, specifically when trying to add a custom className. Error Message: Cannot find name 'classes'. TS2304 Below is the code I am currently working with: import React from 'react& ...

Alter the navigation but keep the URL intact without modifying the view

I have an Angular project that includes a login component. The login component is located in the directory app/main/login. I am trying to navigate to the login component from app.component.html using a button. Below is the code snippet from my app-routi ...

Typescript error when using fill or justify prop in React-bootstrap with Typescript

Code import { useCallback, useState, useEffect } from 'react'; import { Tabs, Tab, Spinner, Alert } from 'react-bootstrap'; import { Categories } from '../../models/ICategory'; import IMovie from '../../models/IMovie&apo ...