Conditional type in Typescript can be used to infer tuple types

Why are output1 and output2 not both inferred as [number, number, number] in the following code snippets?

Snippet 1 :

type InferTuple1<T> = T extends any[] ? [...T]: never;

const func1 = <T>(t: InferTuple1<T>) => t;

const output1 = func1([1, 2, 3])

Snippet 2

type InferTuple2<T> = T extends any[] ? [...T]: T;

const func2 = <T>(t: InferTuple2<T>) => t;

const output2 = func2([1, 2, 3]);

output1 is inferred as:

const output1: [number, number, number]

output2 is inferred as:

const output2: number[]
  • I understand the type inference in the first case: the input is an array (extending any[]) so it is inferred as [...T], converting T into a tuple of type [number, number, number]

  • In the second case, the input is still an array, but it appears that the second conditional branch is executed. I am confused as to why this happens, as I would expect Typescript to return the first one since the condition is met.

(Similarly, func1(["foo", 42, "bar"]) is inferred as [string, number, string], while func2(["foo", 42, "bar"]) is inferred as (string | number)[])

Answer №1

When evaluating the conditional type in both cases, it selects the true branch because T extends any[] is true. The distinction lies in how TypeScript infers the generic type argument T when calling either func1() or func2(). In both instances, the argument passed is [1, 2, 3], and the compiler has the flexibility to infer anything compatible with that argument. For func1(), it infers the tuple type [number, number, number], while for func2() it infers the unordered arbitrary-length array type number[]. Both inferences are valid, but different heuristics influence the preference.

The specific workings of this inference process are not extensively documented, so any explanation provided here serves more as an illustration rather than a completely accurate depiction of the process.


TypeScript 4.0 brought about variadic tuple types through microsoft/TypeScript#39094, which allow defining a preference for tuple type inference simply by using [...T] where T is an array-like type parameter.

By defining functions like

<T extends any[]>(t: [...T]) => t
, tuples are favored, whereas
<T extends any[]>(t: T) => t
leans towards arrays.

Moreover, with

<T,>(t: T extends any[] ? [...T] : never) => t
, preferece is shown for tuples. However,
<T,>(t: T extends any[] ? [...T] : T) => t
does not exhibit the same preference due to interference caused by the presence of T. The exact nature of this interference remains unclear, with speculation pointing towards the "strength" of inference candidates affecting the outcome. Nonetheless, playing around with variadic tuples and conditional types can lead to significant inference challenges.


In conclusion, delving into the details of how inference of variadic tuples works through conditional types might shed light on these complexities in the future. Meanwhile, avoiding intricate manipulations involving variadic tuples and conditional types can simplify the inference process. Opting for const type parameters like <const T,>(t: T) => t could be a smoother alternative for inferring tuples in certain scenarios, although its applicability should be assessed based on individual use cases.

Link to Playground

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

Ways to include a link/href in HTML using a global constants file

I have a constants file with a links node that I need to integrate into my HTML or TypeScript file. Constants File export const GlobalConstants = { links: { classicAO: '../MicroUIs/' } } I created a public method called backToClassicAO ...

Error encountered: ⨯ Compilation of TypeScript failed

My current project is utilizing Node.js in conjunction with TypeScript An issue has arisen during compilation within my Node.js application: E:\NodeProjects\esshop-mongodb-nodejs\node_modules\ts-node\src\index.ts:859 ret ...

The Expo TypeScript template highlights JSX errors such as "Cannot assign type 'boolean' to type 'View'. TypeScript error 2322 at line 5:10:5"

Just starting out with Expo and decided to dive in with the typescript template using the npx create-expo-app -t expo-template-blank-typescript command. However, I'm running into some JSX type errors that are popping up even though the Expo server see ...

ERROR Error: Uncaught (in promise): ContradictionError: The variable this.products is being incorrectly identified as non-iterable, although it

Seeking a way to extract unique values from a JSON array. The data is fetched through the fetch API, which can be iterated through easily. [please note that the product variable contains sample JSON data, I actually populate it by calling GetAllProducts( ...

Solving Issues with Names, Modules, and Other Strange Elements in Angular Universal

While the main app runs smoothly, attempting to serve the bundled SSR results in perplexing errors that I'm struggling to comprehend. All setup details are provided below for reference. The process of creating a server-side app seems riddled with sma ...

Encountering the following error message: "E11000 duplicate key error collection"

Currently, I am in the process of developing an ecommerce platform using the MERN stack combined with TypeScript. As part of this project, I am working on a feature that allows users to provide reviews for products. The goal is to limit each user to only o ...

Check if the input values are already in the array and if not, then add

Within my React application, I am displaying an Array and each entry in the Array is accompanied by an input element. These input elements are assigned a name based on the entry's ID, allowing users to enter values. To handle the changes in these inp ...

Please ensure that the table contains all the records corresponding to the respective days

I am struggling with figuring out how to display a record of classes in my table view. The UX prototype I need to follow is shown https://i.stack.imgur.com/CISYn.png (the days of the week are in Portuguese: horario = time, segunda = Monday, terça = Tuesda ...

Creating Typescript type definitions for any object properties

Struggling to find the correct TS syntax with Typescript 3.7.3. I have a random object, for example: var obj = { one: ..., two: ... three: ... }; I want to create a type that includes all keys from that object, like this: type ObjKeys = &ap ...

What is the best way to transfer my static files to the desired output directory in a TypeScript Express application?

I am attempting to transfer my static files from the input directory to the output directory using Express. I found guidance in this tutorial, which utilized shell.js for copying static files. The code responsible for this operation is located in CopyAsse ...

What is the best way to incorporate resources from a different location in an Angular project?

I am facing an issue with the deployment time of my server as I am using @angular/localize to support three languages in my application. Despite all locales sharing the same assets, they are being downloaded and deployed individually for each one. To addr ...

Error in Typescript: 2 arguments were provided instead of the expected 0-1 argument

When attempting to run a mongoose schema with a timestamp, I encountered an error: error TS2554: Expected 0-1 arguments, but got 2. { timestamps: true } Below is the schema code: const Schema = mongoose.Schema; const loginUserSchema = new Schema( { ...

Chai is unable to differentiate between different class types

When using Chai to compare if a returned value of type SimpleModel matches with the expected type SimpleModel, I encountered an error even though my IDE indicated that the types are correct: AssertionError: expected {} to be a simplemodel The setup for t ...

Incorporating fresh components and newly defined attributes in Angular

Is there a way for me to click on a new component button, specify a name, description, select type, and add attributes such as default value and type? I need all this information to be saved and for the new button to appear in the drag-and-drop section. ...

Enhance your React Native experience with IntelliSense recommending react-native/types over react-native

I am trying to bring in <View from react-native, but instead, I am getting react-native/types https://i.sstatic.net/FeRKT.png How can I resolve this issue? This is a new project starting from scratch and I followed the documentation by adding TypeScri ...

I'm curious, which redux architecture would you recommend for this specific scenario?

I'm currently developing an application and I'm facing a challenge in implementing Redux effectively in this particular scenario. Unfortunately, due to restrictions at my workplace, normalizing data is not an option. Let's consider the foll ...

Querying Cloud Firestore with User ID

I'm facing an issue with retrieving a subset of data based on the first letter of the name and including the UID of the document. No matter what I try, it just returns empty data. fetchDataByFirstLetter(firstLetter: string) { this.afs.collection(&a ...

Encountering issues while attempting to run an npm install command on the package.json file

Having trouble running npm install to set up my Angular project on a Mac. It seems like the issues are due to working with an older project. npm ERR! code ERESOLVE npm ERR! ERESOLVE could not resolve npm ERR! npm ERR! While resolving: @angular-devkit/< ...

Updates made in the type declaration files are not being displayed

I'm currently working on an express app and I have been trying to add a new property to the Request's class instance. To achieve this, I created a type.d.ts file at the root of my project that looks like this: type User = { name: string } de ...

Having trouble getting Next.js 404 page to function properly with the .tsx extension?

My latest project involved creating a Next.js application using regular JavaScript, which led to the development of my 404 page. 404.js import { useEffect } from "react"; import { useRouter } from "next/router"; import Link from " ...