How come TypeScript tuples support the array.push method?

In the TypeScript code snippet below, I have specified the role to be of Tuple type, meaning only 2 values of a specified type should be allowed in the role array.

Despite this, I am still able to push a new item into the array. Why is the TS compiler not enforcing this restriction?

let person: {
    name: string;
    age: number;
    hobbies: string[];
    role: [number, string]      //tuple type
} = {
    name: 'John Doe',
    age: 25,
    hobbies: ['coding', 'gaming'],
    role: [1, 'developer']
};

person.role[2] = 'designer';  //This operation is not allowed, as expected
person.role.push('tester'); //This should ideally be prevented by the TS compiler

Answer №1

To achieve this, we must develop a versatile utility type that generates a tuple of the desired length without using mutable Array.prototype methods.

Take a look at this:

type WithoutIndex<T> = Omit<T,number>

type MutableProps = 'push' | 'splice' | 'shift' | 'unshift' | 'pop' | 'sort'

type Tuple<
    Length extends number,
    Type,
    Result extends Type[] = []
    > =
    (Result['length'] extends Length
        ? WithoutIndex<Omit<Result, MutableProps>>
        : Tuple<Length, Type, [...Result, Type]>)

Tuple recursively invokes itself until the length of the Result matches the provided Length argument. Here is the JavaScript representation:

const Tuple = (length: number, result: number[] = []) => {
    if (length === result.length) {
        return result
    }
    return tuple(length, [...result, 1])
}

WithoutIndex simply prohibits the use of any numeric indexes that do not exist in the tuple.

Let's test if it functions properly:

type WithoutIndex<T> = Omit<T, number>

type MutableProps = 'push' | 'splice' | 'shift' | 'unshift' | 'pop' | 'sort'

type Tuple<
    Length extends number,
    Type,
    Result extends Type[] = []
    > =
    (Result['length'] extends Length
        ? WithoutIndex<Omit<Result, MutableProps>>
        : Tuple<Length, Type, [...Result, Type]>)


let person: {
    name: string;
    age: number;
    hobbies: string[];
    role: Tuple<2, string>
} = {
    name: 'stack overflow',
    age: 30,
    hobbies: ['reading', 'jogging'],
    role: ['reading', 'jogging'],
};

person.role[1] = 'reader';  //ok
person.role[10] = 'reader';  // expected error
person.role.push(); //allowed, TS compiler should prevent it
person.role.sort // expected error

Interactive Playground

Diverse tuple

type WithoutIndex<T> = Omit<T, number>

type MutableProps = 'push' | 'splice' | 'shift' | 'unshift' | 'pop' | 'sort'

type DiversityTuple<T extends readonly any[]> = WithoutIndex<Omit<T, MutableProps>>

const diffTUple: DiversityTuple<[string, number]> = ['hello', 42]

diffTUple[0] = 'a' // ok
diffTUple[1] = 1 // ok

diffTUple[10] = 1 // expected error
diffTUple[0] = 1 // expected error
diffTUple[1] = 'b' // expected error


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 implement switchMap when dealing with a login form submission?

Is there a better way to prevent multiple submissions of a login form using the switchMap operator? I've attempted to utilize subjects without success. Below is my current code. import { Subject } from 'rxjs'; import { Component, Output } ...

The Nestjs ClientMqtt now has the capability to publish both pattern and data to the broker, as opposed to just sending

I am currently utilizing Nestjs for sending data to a Mqtt Broker. However, I am facing an issue where it sends both the pattern and data instead of just the data in this format: { "pattern": "test/test", "data": " ...

Implement a personalized Laravel Dusk selector with the attribute data-dusk

In the world of Laravel Dusk, the default selector hunts for the dusk="something" attribute in your HTML. If you want to dive deeper into this topic, check out this resource. However, when it comes to compatibility with Typescript for React/Vue, ...

Out of nowhere, encountering TS2322 Typescript errors, causing type mismatches during the compilation phase

I am facing an issue with AWS Codebuild while deploying APIs written in lambda and exposed via API Gateway using Koa. The build process is throwing an error related to type assignment. src/components/chart-color-settings/chart-color-settings.ts(11,13): err ...

A guide on implementing directives in Angular 2

I am trying to load my navbar.html in my app.component.html by using directives and following the method below: Here is my navbar html: <p>Hi, I am a pen</p> This is my navbar.ts: import {Component, Directive, OnInit} from '@angular/c ...

Is there a specific type in typescript that represents every iterable object?

We have a unique function shown below: export const transformUndefinedToNull = (obj) => { const convert = (o) => { Object.keys(o).forEach((key) => { const value = o[key]; if (value === undefined) { o[key] = null; } ...

Is it possible to utilize an XML format for translation files instead of JSON in React Native?

I'm in the process of creating a react native application using the react i18next library. For translations, I've utilized XML format in android for native development. In react native, is it possible to use XML format for translation files inste ...

TypeScript: "The type is generic and can only be accessed for reading." - Error code 2862

Consider this sample JS function that requires type annotations: const remap = (obj) => { const mapped = {}; Object.keys(obj).forEach((key) => { mapped[key] = !!key; }); return mapped; }; I am attempting to add types using generics (in ...

Having trouble with React state not updating?

Hello, I am a beginner in the world of React and currently working on fetching an array of endpoints. My goal is to update the API's status every 15 seconds. Here is the code snippet for the array of endpoints: export const endpoints: string[] = [ " ...

"Encountering a blank page in React Router when transitioning between separate projects

I've come across some discussions about a similar issue like this one, but I haven't been able to resolve my problem. In my initial project, I can smoothly navigate between its pages. However, when I created a second project hosted in a subdirec ...

Set up a SQS queue to receive notifications from an SNS topic located in another AWS account by using AWS CDK in TypeScript

Looking to establish a connection between an SQS queue and an SNS topic located in a different account using CDK (TypeScript). Presented below is the code snippet (contained within a stack) that I believe should facilitate this integration. However, I have ...

Key factors to keep in mind when comparing JavaScript dates: months

Check the dates and determine if the enddate refers to the following month by returning a boolean value. Example startdate = January 15, 2020 enddate = February 02, 2020 Output : enddate is a future month startdate = January 15, 2020 enddate = January 2 ...

Data binding in Angular 2: Connecting components

Is it possible to establish a connection between two components that are working with related objects? One of the components is dedicated to filtering, while the other displays the data results. By applying filters such as checkboxes, the displayed data ...

What is the process by which Angular 2 handles imports?

Currently diving into the world of Angular2 with TypeScript. I understand that SystemJS is crucial for enabling the import feature, like this example: import { bootstrap } from "angular2/platform/browser"; While this all makes sense, I find myself questi ...

Using Typescript/JSX to assign a class instance by reference

Looking to access an object's property by reference? See the code snippet below; class Point{ x:number; y:number; constructor(x,y) { this.x=x; this.y=y; } } const a = { first: new Point(8,9), second: new Point(10,12) }; let someBoo ...

Having trouble deleting JavaScript object properties within a loop?

Struggling to comprehend the behavior of this particular piece of javascript code. const devices = searchResult.results.forEach(device => { const temp = Object.keys(device.fields); for(var property in temp) { if(device.fields.hasOwnPro ...

Having trouble retrieving information from a JSON object? Unable to retrieve property 'company_about' of an undefined object?

Here is the JSON data I have: [ { "id": 1, "job_id": 1, "company_profile": "Sales and Marketing", "company_about": "Established in 1992 , it is a renouned marketing company", "company_product": "Ford,Mustang,Beetle", "key_skills": ...

Troubleshooting notifications generated by react-hook-form and zod

My customer registration form is causing all my error messages to show as "required." I suspect it might be a misconfiguration in zod or react-hook-form. Below, you'll find the code snippets. This is my generic input component: import { DetailedHTMLP ...

Saving JSON format in VueX State Management

I'm relatively new to using Vue/VueX and I am exploring methods for storing JSON data in the VueX state. Initially, it seemed like a simple task: state { jsonthing: { ... } } However, I encountered an issue where getters return an Observer type ins ...

Utilize the provider within the decorator function

Essentially, the challenge I am facing is passing an authService to the "verifyClient" function within the @WebSocketGateway decorator. Here is how it should look: @WebSocketGateway({ transports: ['websocket'], verifyClient: (info: { req: Inc ...