What is the proper way to define a tuple type with a specific size N for the vector class in C++?

I am seeking to create a tuple type with a fixed size N, allowing for functionality such as:

let tuple: Tuple<string, 2> = ["a","b"]

In this scenario, "number" represents the type T, and "2" denotes the size N. Subsequently, I aim to develop a mathematical Vector class that not only embodies the tuple but also integrates additional methods like vector addition:

class Vector<N extends number> implements Tuple<number, N> {...}

While exploring various solutions for implementing the tuple type, I encountered several challenges. The simplest approach I came across (referenced here) entailed utilizing an interface structured as follows:

interface Tuple<T, N extends number> extends ArrayLike<T> //or Array<T> {
   0: T
   length: N
}

However, this particular implementation permits access to elements at invalid indices without triggering compiler errors:

let tuple: Tuple<number, 2> = [1,2]
tuple[4] //Compiler does not flag the out-of-bounds index.

An alternative implementation, found here, utilizes recursive conditional types:

type Tuple<T, N extends number> = N extends N ? number extends N ? T[] : _TupleOf<T, N, []> : never;
type _TupleOf<T, N extends number, R extends unknown[]> = R['length'] extends N ? R : _TupleOf<T, N, [T, ...R]>;

While resolving the index issue, this method poses challenges when integrating into the Vector class due to it being a type union, resulting in the following error:

A class can only implement an object type or intersection of the object types with statically known members.ts(2422)

Is there any viable approach to define a tuple type as intended?

Additional query: Is it possible to implement the tuple type sans the array class's inherent methods? Eliminating unrelated methods like filter and sort, especially those impacting the tuple's size, would be advantageous. Using ArrayLike in place of Array could address this concern, while Omit is another potential workaround albeit involving manual specification of each undesired method.

Answer №1

It's clear that when dealing with TypeScript, certain limitations arise in terms of extending or implementing interfaces and classes due to statically known keys. This pertains specifically to situations where deferred generics are involved.

The compiler only supports static keys for objects like classes and interfaces. Therefore, trying to extend or implement types with deferred generic keys will result in errors:
class Foo<K extends string> implements Record<K, string> { } // Error!
interface Bar<K extends string> extends Record<K, string> { } // Error!

For a type such as Vector<N>, which requires numeric-like keys from 0 up to N-1, defining these keys statically becomes challenging due to the generality of N within Vector<N>. As a result, creating a class or interface for Vector<N> proves difficult.

If every possible number-valued key needs to exist, an index signature can be used. However, excluding properties at keys greater than or equal to N introduces complications that negate the use of an index signature.


A viable solution involves describing Vector<N> instances as a type alias rather than an interface or class. By defining a construct signature under a value called Vector, it's possible to create objects that meet runtime requirements while circumventing the need for strictly known keys.

To put this into practice:


Type Descriptions:

Begin by setting up a recursive conditional definition for a tuple of length N:

type Tuple<T, N extends number> = N extends N ? 
  number extends N ? T[] : { length: N } & _TupleOf<T, N, []> : never;
  
type _TupleOf<T, N extends number, R extends unknown[]> = 
  R['length'] extends N ? R : _TupleOf<T, N, [T, ...R]>;

// Define Vector<N> based on the above:
type Vector<N extends number> =
  Omit<Tuple<number, N>, keyof any[]> &
  {
    length: N;
    vectorMethod(): void;
};

Additionally, describe the constructor like so:

interface VectorConstructor {
  new <N extends number>(...init: Tuple<number, N>): Vector<N>;
}

Runtime-Compatible Class:

Create a class named _Vector that includes an index signature for desired functionality:

class _Vector {
  [k: number]: number | undefined;
  length: number;
  constructor(...init: number[]) {
     // Implementation logic
  }
  vectorMethod() {
    console.log("Functionality here");
  }
}

Ensure Compatibility:

Assert that _Vector constructs instances compatible with the desired type:

const Vector = _Vector as VectorConstructor;

Testing our setup:

const v = new Vector(0, 1, 4, 9, 16, 25); // Vector<6>
/* Expected Output:
{
    length: 6;
    0: number;
    1: number;
    2: number;
    3: number;
    4: number;
    5: number;
    vectorMethod: () => void;
} */

v[3]++; // Okay
v.vectorMethod();
v[10]; // Error

In conclusion, through this approach, we've managed to work around the restrictions imposed by TypeScript without compromising runtime objectives. While this method is effective, it comes with its own set of considerations and maintenance efforts if expansion or implementation scenarios emerge.

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

Selecting any of the bar chart labels will reveal just a two-day timeframe

My bar chart is behaving strangely - when I click on all labels, it only shows two days instead of updating as expected. I suspect it may be due to a bad implementation involving parsing. Can anyone provide assistance? I have created a minimum example on ...

Template for typed variable - `ng-template`

How can the parent component correctly identify the type of let-content that is coming from ngTemplateOutletContext? The current usage of {{content.type}} works as expected, but my IDE is showing: unresolved variable type Is there a way to specify the ...

Testing a click event in Angular that triggers only the navigateByUrl function, utilizing Jasmin and Karma

Need help writing unit test cases for click events in Angular using Jasmine & Karma onClickCancel(): any { this.router.navigateByUrl('login'); } Seeking advice on how to write unit tests for click events that open Material dia ...

What is the best way to decouple the data layer from Next.js API routes?

Currently, I am working on a project using Next.js with MongoDB. My setup involves using the MongoDB client directly in TypeScript. However, I have started thinking about the possibility of switching to a different database in the future and how that would ...

Calling a typed function with conditional types in Typescript from within another function

In my attempt to create a conditional-type function, I stumbled upon this question on Stack Overflow. Unfortunately, it seems that the approach doesn't work well with default values (regardless of where the default value is placed). Following the advi ...

Using FIND to search an array object results in an error: Type 'undefined' is not compatible with type ''

I'm currently attempting to search for an element within one array and then assign it to another array object. However, I keep receiving the following error message: Type 'ClosureSummary | undefined' is not assignable to type 'Closure ...

Creating synchronous behavior using promises in Javascript

Currently, I am working with Ionic2/Typescript and facing an issue regarding synchronization of two Promises. I need both Promises to complete before proceeding further in a synchronous manner. To achieve this, I have placed the calls to these functions in ...

Using TypeScript to consolidate numerous interfaces into a single interface

I am seeking to streamline multiple interfaces into one cohesive interface called Member: interface Person { name?: { firstName?: string; lastName?: string; }; age: number; birthdate?: Date; } interface User { username: string; emai ...

The issue encountered during a POST request in Postman is a SyntaxError where a number is missing after the minus sign in a JSON object at position 1 (line 1

Running my API in a website application works flawlessly, but encountering SyntaxError when testing it in Postman - specifically "No number after minus sign in JSON at position 1" (line 1 column 2). The data is correctly inputted into the body of Postman a ...

Currency symbol display option "narrowSymbol" is not compatible with Next.Js 9.4.4 when using Intl.NumberFormat

I am currently utilizing Next.JS version 9.4.4 When attempting to implement the following code: new Intl.NumberFormat('en-GB', { style: 'currency', currency: currency, useGrouping: true, currencyDisplay: 'narrowSymbol'}); I ...

How to extract and compare elements from an array using Typescript in Angular 6

I have created a new Angular component with the following code: import { Component, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { HttpClient } from '@angular/common/http'; @Compone ...

Type Vue does not contain the specified property

I am encountering an issue where I am using ref to retrieve a value, but I keep receiving the error message "Property 'value' does not exist on type 'Vue'". Below is the code snippet causing the problem: confirmPasswordRules: [ ...

Is it advisable to encapsulate my entire Express server within a TypeScript class?

After working extensively with nodeJs, I have decided to explore developing applications in Typescript. Recently, I came across various blogs (like this one) that recommend wrapping modules and the app's entry point in a class when creating a RESTful ...

Issue with retrieving the positions of two numbers in an array

I encountered a challenge: I have an array of integers nums and an integer target. My goal is to find the indices of two numbers in the array that add up to the specified target. Example 1: Input: nums = [2,7,11,15], target = 9 Output: [0,1] Output: Thi ...

Disable the default animation

Is there a way to disable the default animation of the Select label in my code snippet below? export default function TicketProfile(props: any) { return ( <Container> <FormControl sx={{ ml: 1, mr: 1, minWidth: 220 }}> <Inp ...

Angular 7 ESRI loader search widget focus out bug: finding a solution

I am currently working on implementing an ESRI map using esri-loader in my Angular application. Everything seems to be working fine, but I am encountering an error when typing something into the search widget and then focusing out of it. "Uncaught Typ ...

What is the reason behind typescript making it necessary for me to find a way to set a value of type

function f1() { const v : string = String(); if(v) {alert("IF");} // OK const b : boolean = v; // Type 'string' is not assignable to type 'boolean'. if(b) {alert("BOOLEAN");} } f1(); My approach to this issue involv ...

Modifying an Angular Component Template following an API Response

As someone relatively new to Angular, I come with some experience from working with other JavaScript frameworks like Vue and React. Currently, I am developing an Angular Lab application that interacts with 2 APIs to retrieve information. After receiving th ...

Encountering a duplication issue when redirecting components in Angular2/TypeScript using navigateByUrl

Seeking guidance on implementing the login function where the current component redirects to another one upon clicking the login button. Below are my .ts and .html files: login.component.ts login.component.html The issue arises when using npm start for ...

Issue of displaying buttons based on sibling's height under certain conditions

OBJECTIVE I have undertaken a project to enhance my skills in React and TypeScript by developing a UI chat interface. The design requirement is that when a chat message has enough vertical space, its action buttons should appear stacked vertically to the ...