Exploring the process of inferring union types in TypeScript

const type p1 = { a: number, b: string }
const type p3 = { a: string }
const type p4 = p1 | p3

let sample: p4 = { a: '123', b: '123' }

function checkP3(obj: p4): obj is p3 { 
    return typeof (<p3>obj).a === 'string'
}

function functionTest(obj: p4) {
    if ('b' in obj) {
        // Uncaught TypeError: obj.a.toFixed is not a function
        obj.a.toFixed() //<- This now does not throw an error
    } else { 

    }
}
functionTest(sample)

What is the reason behind the absence of an error when initializing the sample variable? User-Defined Type Guards

Answer №1

In the world of TypeScript, there exists an issue (microsoft/TypeScript#20863) regarding union types not being discriminated unions. This means that when the compiler encounters a union type without discrimination, it does not split the union to perform excess property checking as one might expect. Despite this, the compiler overlooks excess properties within the union and doesn't raise errors if at least one member allows the property. Excess property checking is a convenience more than a necessity for type safety in TypeScript. Object types in TypeScript are considered open, allowing additional properties to be added without breaking the defined type. This flexibility comes with the consequence that object types are not exact, meaning that having extra properties doesn't invalidate the type. Therefore, distinguishing between types solely based on the presence or absence of a certain property may not work as expected. To address this issue, explicitly define the properties of each type to avoid ambiguity. By clearly specifying the properties, TypeScript's discriminated union mechanism can accurately identify and report errors. For instance, by defining the p3 type as { a: string, b?: undefined }, TypeScript can now enforce type distinctions and properly handle excess property checks. By taking these measures, TypeScript's compiler can better identify type discrepancies and prevent runtime errors. Creating well-defined types with clear properties is key to ensuring type safety in TypeScript. Remember, TypeScript is a powerful tool, but its effectiveness depends on how types are defined and utilized. Good luck coding!

Answer №2

Implement your own type guard function for narrowing down types instead of checking 'b' in obj

if (!isP3(obj)) {
    obj.a.toFixed() // Error
} else {

}

Regarding the assignment

let demo: p4 = { a: '123', b: '123' }
, it's also concerning that no error is thrown. It seems to work properly with an error only if we define a as a boolean instead of a number. The assignment seems to fail only when the discriminator type includes a union type. For more details, you can follow the progress of this issue here

The section of the specification on this issue doesn't fully clarify the current behavior. It seems like an in-depth explanation can be found by delving into the compiler itself. Here is the link to the relevant section in the TypeScript specification.

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

employing constructor objects within classes

I am attempting to utilize a class with a constructor object inside another class. How should I properly invoke this class? For example, how can I use Class 1 within Class 2? Below is an instance where an object is being created from a response obtained f ...

Tips for incorporating a hashbang into a JavaScript file that is executable without compromising browser compatibility

Is it possible to run code like this in both Node.js and the browser? #! /usr/local/bin/node console.log("Hello world") I have a script that I currently run locally in Node.js, but now I want to also execute it in the browser without having to modify it ...

Issue with for loop execution within subscribe event

In my chat design, there is a list of people on the left side. When a user clicks on any person, I display their chat history on the right side. To achieve this, I need to transfer user details from one component to another using an RXJS subscribe call. Da ...

Having trouble with Axios cross-origin POST request CORS error in React / Typescript, even after trying all the common solutions

I am encountering a CORS error in my React / Typescript project when trying to make a POST request using Axios. The project uses a Node.js / Express backend. Despite researching common CORS errors and reading highly-rated posts on the topic, I have been un ...

There was an issue: Control with the name 'name' could not be located

Whenever I submit the form and try to go back, an error pops up saying "ERROR Error: Cannot find control with the name: 'name'". I'm not sure what I might be missing. Do I need to include additional checks? Below is my HTML file: <div ...

I encounter an error in my JavaScript function indicating that it is not defined

let element = document.querySelector("#value"); let buttons = document.querySelectorAll(".btn"); buttons.forEach(function (button) { button.addEventListener("click", function(event){ console.log(event.currentTarge ...

Changing the button class during an event in Angular 4

In the process of creating an MCQ test, I am looking to implement a feature where selected button options are highlighted in green upon clicking. While I have successfully implemented this feature using Angular 1, I am facing challenges in converting it to ...

Angular UI validation malfunctioning upon loading of the page

My webpage contains multiple rows with specific validation requirements - at least one Key, Time, and Input must be selected. Initially, the validation works as expected. However, after saving and reloading the page, the default selection for Key, Time, an ...

Ngrx effects are not compatible with earlier versions of TypeScript, causing issues with functionality

Seeking assistance with my Ionic 3 App utilizing ngrx/store and ngrx/effects. However, upon attempting to run the app, I consistently encounter the following error: TypeScript Error A computed property name in a type literal must directly refer to a bui ...

I am curious about the types of props for the methods within the 'components' object in react-markdown

Having some trouble using 'react-markdown' in NextJs 13 with typescript. TypeScript is showing errors related to the props of the 'code' method, and after searching online, I found a solution that involves importing 'CodeProps&apos ...

Merge attributes from objects within an array

I am seeking assistance with a basic task in TypeScript as a newcomer to the language. My challenge involves manipulating an array of objects like this: // Sample data let boop = [ {a: 5, b: 10}, {a: 7, c: 8}, {a: 6, b: 7, c: 9} ]; My objectiv ...

How can I integrate React-Router Link as a component within Material-UI using Typescript?

Hey everyone, I've encountered an issue while trying to utilize Material UI's component prop to replace the base HTML element of a component. Error: The error message reads: Type 'Props' is not generic. This problem arises from the fo ...

Tips for effectively passing query string parameters in Angular

I am looking to make an HTTP request with parameters through a query For instance: URL: https://api/endpoint?d=1&value=2 ...

Resetting the timer of an observable in Angular 2

I have a unique service that automatically calls a method every 30 seconds. There is also a button that allows the user to reset the timer, preventing the method from being called for another 30 seconds. How can I make sure that the method does not get cal ...

Unable to use global modules in NestJS without importing them

Currently, I am in the process of integrating a global module into my nest.js project I have written a service as shown below: export interface ConfigData { DB_NAME: string; } @Injectable() export class ConfigManager { private static _inst ...

Is there a way to send both a file and JSON data in a single HTTP request?

Once I developed a small application using NestJs where I implemented a BFF (Backend for Frontend) service. Within this service, I tried to execute a POST request to create a new user while also including the user's avatar in the same request. Here is ...

What is the best way to integrate Greensock CustomEase with TypeScript?

Currently, I am developing a game using Typescript with PixiJS and GreenSock (or is it GSAP?) for handling all the tweening. I recently encountered a situation where I needed to implement some custom easing using cubic-bezier curves. GreenSock provides a C ...

Unable to load the default value for ion-select in TypeScript

I have reviewed this question, this question, and this question. However, none of them seem to provide a straightforward solution for what I am searching for. <ion-list> <ion-item> <ion-label>Select Book</ion-label> <i ...

Using Angular's ElementRef to set focus on an ion-textarea: "The 'setFocus' property is not found on the 'ElementRef' type."

After developing a textarea component that automatically focuses itself when created using the ngAfterViewInit() method, everything seemed to be working perfectly as expected. ngAfterViewInit() { if(this.text.length===0){ this.theinput.setFocus(); ...

Merely using Array.isArray check is insufficient to prompt the TypeScript compiler about a potential array value

I have a method where the type can be an array, but I need to verify that it is not an array before accessing the argument. However, despite my check, I am still encountering the following error (excerpt) on line this.setState({ cuisine });: The type &ap ...