What causes two variables of identical types to exhibit varying behaviors based on their creation methods?

What causes the types of tuple and tuple2 to be different even though both nums and nums2 are of type (0 | 1 | 2)[]?

// const nums: (0 | 1 | 2)[]
const nums: (0 | 1 | 2)[] = [];

// let tuple: (0 | 1 | 2)[]
let tuple = [nums[0], nums[1]];


// const nums2: (0 | 1 | 2)[]
const nums2 = ([] as {num?: 1 | 2}[]).
    map(n => typeof n.num === "undefined" ? 0 : n.num)

// let tuple2: number[]
let tuple2 = [nums2[0], nums2[1]];

Playground

Answer №1

The distinction between widening and non-widening literal types, as seen in the changes made in microsoft/TypeScript#11126, is a consequence to be aware of. It can be confusing because IntelliSense does not visually differentiate these types; it's an intrinsic attribute of the type itself. For instance, it's not easy to determine if 0 | 1 | 2 represents widening or non-widening.


In general, a literal type appearing solely in an expression (and not in a type) will automatically widen to its base primitive type counterpart when there is a possibility for reassignment. This means that 0 will become number, as demonstrated in this example:

const nums = ([] as { num?: 1 | 2 }[]).map(n => typeof n.num === "undefined" ? 0 : n.num)
// const nums: (0 | 1 | 2)[]

Here, the type of nums broadens to include both literal types because implicit type definitions are used in the map() method, resulting in a widened array of literal types. However, specifying the type explicitly in a type annotation prevents this widening behavior:

const nums: (0 | 1 | 2)[] = [];
// const nums: (0 | 1 | 2)[];

To prevent auto-widening of literal types, you can provide explicit hints in your code. For instance, by manually typing values in expressions or specifying the type parameter during function calls. More details on how to manage this behavior can be found in this discussion within microsoft/TypeScript#12267.

Link to Code Playground

Answer №2

In my opinion, TypeScript interprets the following code snippet:

let partOfSomeNums2 = [someNums2[1], someNums2[0]];

as

let partOfSomeNums2 = [0, 1];

The second line defines partOfSomeNums2 as a numeric array, just like the first line.

An alternative approach would be:

let partOfSomeNums2 = [...someNums2.slice(1, 2), ...someNums2.slice(0, 1)];

Or even:

let partOfSomeNums2: (0 | 1 | 2)[] = [someNums2[1], someNums2[0]];

I believe your someWorkingFunction in your playground is functional because the type 0 | 1 | 2 is derived directly from an enum, unlike your non-functional code where the value 0 is hardcoded.

UPDATE

If it were up to me, I would opt for this solution: Playground

To implement this strategy: type SmallNumber = 0 | 1 | 2; Subsequently, employ this type in your mapping function:

const someNums2 = someObjects2.map((obj): SmallNumber =>
    typeof obj.num === "undefined" ? 0 : obj.num
);

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 click event handler functions using TypeScript

I encountered an issue when trying to set up an event listener for clicks. The error message I received was that classList does not exist on type EventTarget. class UIModal extends React.Component<Props> { handleClick = (e: Event) => { ...

Error: React-Redux Provider is being called incorrectly

I am currently working on a small application to get familiar with using Redux Toolkit. My understanding of React/Redux mainly comes from an outdated Udacity course. Although the error message lists the top 3 reasons for this particular error, none of the ...

What could be causing the lack of updates for my service on the app component?

I am currently using Ionic 4 and facing an issue with displaying information about the logged-in user. The code works perfectly in all components except for the app component. I have a variable named userData which is a BehaviorSubject. Can someone help me ...

The index access type cannot be used with T[Key extends keyof T]

My work frequently involves arrays structured like this: [ {key1: val1, key2: value2, key3: val3}, {key1: val1, key2: value2, key3: val3}, {key1: val1, key2: value2, key3: val3}] and I often need to convert them into a dictionary/map format, for example: ...

Creating a mapping for a dynamic array of generic types while preserving the connection between their values

In my code, I have a factory function that generates a custom on() event listener tailored to specific event types allowed for user listening. These events are defined with types, each containing an eventName and data (which is the emitted event data). My ...

Transform a collection of objects into instances of a class

I have a scenario where I am fetching an array of objects from PHP and my goal is to transform this data into instances of a class called ContentData. The current solution that I have seems to work fine, but deep down I sense that there might be a more el ...

MaterialUI Divider is designed to dynamically adjust based on the screen size. It appears horizontally on small screens and vertically on

I am currently using a MaterialUI divider that is set to be vertical on md and large screens. However, I want it to switch to being horizontal on xs and sm screens: <Divider orientation="vertical" variant="middle" flexItem /> I h ...

Guide on integrating a plain Service/Provider into nest.js

I recently created a basic TypeScript class in nest.js called JwtTokenService.js. // JwtTokenService.js import { Injectable, Optional } from '@nestjs/common'; import { JwtService } from '@nestjs/jwt'; import { JwtPayload } from ' ...

Trouble encountered while using useRef in TypeScript

I'm encountering an issue with the code below; App.tsx export default function App() { const [canvasRef, canvasWidth, canvasHeight] = useCanvas(); return ( <div> <canvas ref={canvasRef} /> </div> ) ...

Errors in TypeScript Compiler for using react-bootstrap in SPFx project

After setting up an SPFX Project with Yeoman generator and including react-bootstrap as a dependency, I encountered errors when using react-bootstrap components and running gulp serve. The error messages are as follows; does anyone have any solutions to ...

Testing a function that utilizes Nitro's useStorage functionality involves creating mock data to simulate the storage behavior

I have developed a custom function for caching management, specifically for storing responses from API calls. export const cache = async (key: string, callback: Function) => { const cacheKey = `cache:${key}`; const data = await useStorage().get ...

Checking for unnecessary properties in Typescript with vue-i18n

Let's consider two different languages represented in JSON format: jp.json { "hello": "こんにちは" } ge.json { "hello": "hallo", "world": "welt" } Now, we are going to com ...

Experiment with Google Sign-In authentication in jest with Firebase

As I try to effectively mock firebase authentication with Google login, I am encountering some difficulties. Below is the code that I am currently working with: simple.tsx import React, { Component } from 'react'; import * as firebase from &apo ...

Implementing Service Communication

I created an Angular Application using the Visual Studio Template. The structure of the application is as follows: /Clientapp ./app/app.module.shared.ts ./app/app.module.client.ts ./app/app.module.server.ts ./components/* ./services/person-data.service. ...

Customizing MUI Themes with TypeScript: How do I inform TypeScript that the theme is provided by the provider?

Below is a modified demo code snippet extracted from Material UI documentation: function ThemeUsage() { const theme = { palette: { primary: { main: "#000", }, }, } as const; type DefaultThemeType = { theme: type ...

What is the best way to determine the property type dynamically in TypeScript based on the value of another property?

I have been working with Polymorphic relationships and currently have the following TypeScript interface defined: interface SubjectA {} interface SubjectB {} interface SubjectC {} enum SubjectType { SubjectA = 'Subject A', SubjectB = 'S ...

Trouble accessing the Ionic SQLite database: Unable to open

I encountered some errors while attempting to connect my ionic app to a database. I am currently running the app on an android device using Google Chrome DevTools to troubleshoot the issue. Check out the createDatabase() function and the specific error th ...

Visual Studio - Error TS1005 'Unexpected token'

After spending nearly 5 hours scouring the internet for a solution, I am still unable to resolve this persistent issue. The responses I've found so far do not seem to address the specific problem I'm facing. Although I have upgraded the tsc vers ...

Storing redux dispatch action using the useRef hook in Typescript

Currently, I am attempting to store the redux action dispatch in a React reference using useRef. My goal is to be able to utilize it for aborting actions when a specific button is clicked. Unfortunately, I am facing challenges with assigning the correct ty ...

What is the method for extracting user input from a text box on a webpage?

Having trouble with retrieving the value from a text box in my search function. SearchBar Component import { Component, OnInit, Input } from '@angular/core'; @Component({ selector: 'app-search', templateUrl: './search.compon ...