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

Ensuring type compatibility in a declarative manner: What are the methods?

My goal is to establish a compile-time constraint that ensures a variable of type T2 can be assigned to a variable of type T1. If I have a value (t2) of type T2, I can simply do the following: const t1: T1 = t2; Is there an alternative method to achiev ...

Enhance the visual appeal of your checkboxes in React Fluent UI by customizing the color of the checked mark and

I have a React application using Fluent UI. Currently, the <Checkbox/> component is displaying with its default colors and behavior like this: I want to customize the color of the checked mark and label (Green for checked mark and brown for label). ...

Dealing with throwing Exceptions in jest: A guide for developers

I have developed a method that throws an exception when the provided password does not match a regex pattern. I attempted to handle this in Jest. it('Should prevent insertion of a new user if the password doesn't match the regex', async () ...

Trouble encountered when attempting to call a function within another function in StencilJS

I am currently following a tutorial on building a drag and drop file uploader using StencilJS for some practice and fun. However, I have encountered an error in the code. Below is a snippet of the code, but I can provide more if necessary. @Component({ ...

select items using a dropdown menu in an Angular application

Let me describe a scenario where I am facing an issue. I have created an HTML table with certain elements and a drop-down list Click here for image illustration When the user selects in, only records with type in should be displayed Another image refere ...

parsing a TypeScript query

Is there a simpler way to convert a query string into an object while preserving the respective data types? Let me provide some context: I utilize a table from an external service that generates filters as I add them. The challenge arises when I need to en ...

Issue with Angular 2 Observable not triggering the complete function

I've been experimenting with the hero app tutorial for Angular 2 and currently have this Component set up: import { Component, OnInit } from '@angular/core' import { Subject } from 'rxjs/Subject'; import { Hero } from "./hero"; im ...

Simplifying parameter types for error handling in app.use callback with Express.js and TypeScript

With some familiarity with TypeScript but a newcomer to Express.js, I aim to develop a generic error handler for my Express.js app built in TypeScript. The code snippet below is functional in JavaScript: // catch 404 and forward to error handler app.use((r ...

Having trouble linking tables to Node.js with TypeScriptyntax?

I am facing an issue with mapping multiple entities using sequelize. I keep encountering the error message " Error: Profesor.hasOne called with something that's not a subclass of Sequelize.Model". How can I resolve this issue? Below is the code for t ...

What is the proper classification for me to choose?

Apologies for the lack of a suitable title, this question is quite unique. I am interested in creating a function called setItem that will assign a key in an object to a specific value: const setItem = <T, U extends keyof T>(obj: T) => (key: U, ...

The VueJS function is not defined

Looking for a way to fetch data from graphql in my vue project and store it in a variable. The function is asynchronous and the value of rawID needs to be awaited. However, there is a possibility that it could result in undefined, causing an error in the ...

The argument labeled as 'shop' does not fit the criteria for the parameter 'items' or 'cart'

I've been doing some research but haven't had any luck finding a solution. I'm fairly new to Angular and currently working on a webstore project. I followed a tutorial, but encountered an error along the way. import { select, Store } from & ...

Unlock the Power of EmailJS with Vue.js 2 and TypeScript

I couldn't find a similar issue online, so here's my problem. I'm trying to create a form for receiving contact from an app using Vue.js 2 and TypeScript. Here is my code: <form ref="form" class="form-data" @submit.pr ...

Issue TS8011 in Angular 6 is related to the restriction on using type arguments only in files with the .ts extension

I have a project in Angular 6 where I need to integrate a JS library. This library is confidential, so I can't disclose its details. The problem I'm facing is that the TypeScript compiler seems to misinterpret characters like <<24>>, ...

The validator function in FormArray is missing and causing a TypeError

I seem to be encountering an error specifically when the control is placed within a formArray. The issue arises with a mat-select element used for selecting days of the week, leading to the following error message: What might I be doing incorrectly to tri ...

Struggling to import a React component for sharing purposes

I've developed a React component that I want to share through a locally hosted npm repository. To achieve this, I created the React component using typescript, transpiled it to JavaScript, and then published the resulting code to the repository. Howe ...

Issue with updating Angular list reference when deleting an item

My current task involves implementing a feature that displays selected items from a hierarchical structure on the right side. slice.component.ts : import { Component, Input, OnInit, ChangeDetectionStrategy, ChangeDetectorRef } from '@angular/core&a ...

Using TypeScript with slot props: Best practices for managing types?

In my current project, I'm utilizing slot props. A key aspect is a generic component that accepts an Array as its input. This is the structure of MyComponent: <script lang="ts"> export let data: Array<any>; </script> ...

What distinguishes ES6 from ES2015 in the TypeScript compiler option `--libs`?

Can you explain the distinction between ES6 and ES2015 in the TypeScript compiler option here? Also, what does --libs do? ...

Show a roster of individuals by inputting values that will populate the list with their names

I'm attempting to showcase a list of users by taking the value from an input and using it as a parameter in a get() method. After receiving the response from the get() method, I am pushing it into an object and then trying to display this object in th ...