Which is better for narrowing object property types: using dot notation or object literal notation?

Is there a reason why type narrowing by assignment behaves differently based on whether we use dot notation or object literal notation? Below are two examples to illustrate this discrepancy.

type MyObj = { x?: number | string }

let obj1: MyObj = {}
obj1.x = 1 // assignment using dot notation
obj1.x // returns number

let obj2: MyObj;
obj2 = { x: 1 }; // assignment using object literal notation
obj2.x // returns number | string | undefined

I anticipated consistent behavior in both cases.

Answer №1

Narrowing only occurs on union types.

In the first scenario, when you assign directly to the property x, which has a union type of number | string | undefined, the control flow narrows it down to number within the scope. However, in the second scenario, when you assign to an object that is not a union, the control flow does not narrow it.

The narrowing is limited to the current scope, and will not persist in a nested function, for example.

I understand your point, it may seem like TypeScript could handle this situation, but unfortunately, it currently does not. The control flow analysis is constantly being improved.

If you change your type to a union, then assigning a literal object will cause narrowing to occur:

type MyObj = { x?: number | string }

let obj1: MyObj = {}
obj1.x = 1 // assign x with dot
obj1.x // number

function anotherScope() {
    obj1.x // number | string | undefind
}

let obj2: MyObj;
obj2 = { x: 1 }; // assign x with object literal
obj2.x // number | string | undefind

obj2.x = 2;
obj2.x // number

let obj3: { x: number  } | { x: string } | { x?: undefined };
obj3 = { x: 1 } ; // assign x with object literal
obj3.x // number, narrowed to first member of the union


let obj4: { x?: number  } | { x?: string } 
obj4 = { x: 1 } ; // assign x with object literal
obj4.x // number | undefind, narrowed to first member of the union

Playground

These are the relevant issues discussing the design limitation in TypeScript: 1. 2.

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

Is there a way for me to incorporate a feature that verifies whether an email address is already registered before allowing the person to sign up?

I am currently working with Node.js, express.js, mongoose, and pug to develop a registration/login system. I have successfully stored the name and email in a mongoose database with specified schema for these fields. The data is sent from a pug page via a p ...

What could be causing my click() function to only work properly after resizing?

It's driving me crazy. The code snippet below works perfectly, but not in my project. Only the code in the resize() function seems to work. When I resize the window, everything functions as expected - I can add and remove the 'open' class by ...

Enhancing performance by dynamically updating DOM elements when they come into view during scrolling

Currently, I am in search of an efficient algorithm to dynamically load background-images for a group of <li>'s but I am encountering some efficiency issues. The code I am using at the moment is as follows: function elementInView($elem, vps, vp ...

Are you experiencing issues with the map displaying inaccurate latitude and longitude when clicking on a specific point?

I've successfully created a simple polyline on Google Maps and attached a click event listener to it. However, I'm encountering an issue where clicking on the line provides me with latitude and longitude coordinates that point to Canada, even th ...

Guide on how to retrieve the vertex information once an object is loaded using the ObjLoader in Three.js

When using ObjLoader in Three.js to load a *.obj file into my scene, everything works fine. However, I'm uncertain on how to manipulate the object's vertices and indices individually. How can I access the vertices of the loaded *.obj model? Atte ...

How can I display a spinner/loader gif when the page loads using Vue?

When it comes to loading components on a webpage, jquery has the options of $( document ).ready() and onload. But in Vue, how can we achieve the same effect? For example, when a user clicks our page, how can we display a spinner until all contents are load ...

Uncertainties surrounding the use of JSON data versus a JavaScript object

As a newcomer to programming, I have ventured into Stack Overflow and W3schools to enhance my skills. Recently, I created a small project for educational purposes. One dilemma is bothering me - is the JSON file I generated actually in proper JSON format o ...

Instead of presenting MySQL data in tables on an HTML page, showcase it in editable text boxes

I have successfully imported data from my database table into an HTML table, but now I want to display them in locked text boxes. When the user clicks an "edit" button, the corresponding text box should become unlocked so that they can edit the data and sa ...

Angular input box with integrated datepicker icons displayed inside

Currently, I have an input field and a datepicker displayed in a row. However, I need to show an icon inside the input box instead. Here is my code: <div class="mb-2" style=" float: left;" class="example-full-width" class= ...

Extract data from JSON using the parse function

After utilizing the function JSON.stringify(), I have obtained this JSON: {"get":"function (f,g){'use strict';var h,i;if(i={},'string'==typeof f){if('object'==typeof g)for(h in g)i[h]=g[h];i.url=f}else if('object'== ...

How to dynamically insert li elements into a ul list using the .after() method

After trying to add an li element to the ul list, I noticed that my DOM wasn't updating when checking the page source. Even attempting to delete elements with the delete button didn't seem to work as expected. Here is the code snippet below. ...

Use Express JS to enable users to upload their profile picture either during account creation or on the registration form

Just starting out with node js, I'm working on a basic app where users can create accounts with their details. I've implemented mongo dB as the backend to store user information such as names and emails. Once an account is created, users are dir ...

Unable to capture HTML form input in $_POST[]

I've encountered an unusual issue while transferring data from an email form (HTML5) to ajax/JSON and receiving false in order to prevent redirection to the php script after pressing the submit button. When I include commas between each data paramete ...

Is it possible to include additional information when creating a subscription for a customer on Stripe using Node.js

I am facing an issue with adding metadata to the customer object during the creation of a new subscription/customer using Stripe. The problem lies in the fact that the metadata is not being saved to the customer object. I have checked the logs/events in St ...

Eliminate unnecessary scrollbar from fancybox iframe

I am trying to display content in an iframe using Fancybox, but I am encountering an issue. Despite all the content being contained within the iframe, horizontal and vertical scroll bars are appearing. When inspecting the element in Firefox, I noticed th ...

Extracting information from an Observable in Angular: A comprehensive guide

I am currently working on an Angular application that interacts with a server through RESTful requests, and receives a JSON stream response containing objects of a specific type. The interface for these objects is as follows: export interface Personal { ...

Angular 8 bug: Requiring 2-3 arguments, received only 1

Currently deepening my knowledge in Angular and I encountered a situation within one of my services agree(id: string) { const headers = new HttpHeaders('Content-Type: application/json'); return this.HttpClient.put(`${this.apiUrl}/agree/` ...

Should front-end and back-end share Typescript data modeling through classes or interfaces?

I'm currently exploring the best approach to share the same data types between the client (React) and the server (Express + Socket.IO). Within my game, there are various rooms each storing the current status, such as: class GameRoom { players: P ...

Is there a way to transfer the JavaScript function variable value to a Mason variable?

After clicking a button on the client side, I am storing the window.location.href property in a JavaScript variable. Now, I need to send this variable back to the server. How can I retrieve the value of the JavaScript variable in Mason code? Currently, I ...

Remove the input form from the angular app in WordPress after reloading or submitting

Presently, I am encountering an issue with my form: <table class="widefat" ng-app="myApp" ng-controller="MyCtrl"> <tr> <td><label for="name_pokemon"><?php _e( "Name", ...