Using Typescript Type Guard will not modify the variable type if it is set in an indirect manner

TL;DR Differentiation between link1 (Operational) vs link2 (not functional)

TypeGuard

function validateAllProperties<T>(obj: any, props: (keyof T)[]): obj is T {
    return props.every((prop) => obj.hasOwnProperty(prop))
}

Consider a variable msg that can be of 2 varieties - kind1 or kind2.

interface kind1 {
    propertyA: number
}

interface kind2 {
    propertyB: number
}

let msg: any = {
    propertyA: 1
}

In link1 the type guard is accessed directly and functions correctly as expected. In link2 a boolean variable isKind1 is initially set to false and only changes to true if the type guard check is passed.

let isKind1 = false
let isKind2 = false

// Check msg properties
if (
    validateAllProperties<kind1>(msg, [
        "propertyA",
    ])
) {
    console.log("Type: Kind1")
    isKind1 = true
} else if (
    validateAllProperties<kind2>(msg, [
        "propertyB",
    ])
) {
    console.log("Type: Kind2")
    isKind2=true
} else {
    throw `invalid ❌`
}
console.log("Check passed ✅")

Hence in link2 , I cannot envision a scenario where if isKind1 === true but msg is not of type kind1

if (isKind1) {
    // Why is msg not exclusively of type kind1
    console.log(msg.propertyA)
}

Answer №1

In certain scenarios, your code may be correct but Typescript lacks a rule to verify its accuracy. The compiler cannot always confirm every valid property of your code.

To delve deeper into this issue, consider the fact that your first example functions properly in Typescript 4.4+ due to a recent enhancement known as Control Flow Analysis of Aliased Conditions and Discriminants outlined in the release notes. Essentially, there is a specific guideline applied by the compiler in the first scenario where the type guard's outcome directly links with the variable isKind1 in all execution paths. However, this rule does not extend to your second example because the value assigned to isKind1 remains the static value true, which is not considered a type guard.

Answer №2

When considering the scenario, if isKind1 === true, it does not guarantee that msg will always belong to kind1, as isKind1 is declared with let and its value can be modified outside of the specified if statement.

For instance

const msg = {
  "foo": "bar"
}

let isKind1 = false
if (
    hasAllProperties<kind1>(msg, [
        "propertyA",
    ])
) {
    console.log("Kind1")
    isKind1 = true
}
// Assuming the condition in if statement was not met, thus making isKind1 = false and msg not having type kind1

// Modifying isKind1 externally
isKind1 = true

if (isKind1) {
    // Even though isKind1 is set to true now, msg may not necessarily be of type kind1
    console.log(msg.propertyA)
}

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 a data type restricted to utilizing property names exclusively from a specified string union:

I have a specific Enum: enum MyEnum { optionOne = 0, optionTwo = 1, optionThree = 2, optionFour = 3, } and a related Type: export type EnumNamesList = keyof typeof MyEnum; I am looking to create a type similar to this: export type EnumDataTypes = ...

What is the best way to simulate a TypeScript enum in my Jest test suite?

In my unit tests, I need to create a mock of an enum. The original enum structure includes fixed values for 'START' and 'END', but the middle options can change over time to represent new features. These changes may involve adding or re ...

Differences between Typescript, Tsc, and Ntypescript

It all began when the command tsc --init refused to work... I'm curious, what sets apart these three commands: npm install -g typescript npm install -g tsc npm install -g ntsc Initially, I assumed "tsc" was just an abbreviation for typescript, but ...

Discover a Simple Trick to Enhance tsc Output: Unveil the Art of

When I work on a TypeScript project, I typically use the watch mode of the compiler: tsc --watch However, one issue I face is that it's challenging to identify errors in the output since they are displayed as plain text: Sometimes I don't even ...

Challenge with Updating React Components When Switching Themes

In my React application, I'm facing a challenge with theme switching. There are two themes available: Theme One and Theme Two. To dynamically load theme components, lazy loading has been implemented based on the selected theme. Each theme has its own ...

Using Typescript: How to access a variable beyond the scope of a loop

After creating an array, I need to access the elements outside of the loop. I am aware that they are not in the scope and using 'this.' before them does not grant access. colIdx = colIdx + this.columns.findIndex(c => c.editable); this.focusIn ...

Executing the routing component prior to any other tasks

Having an issue where the ProductsService is fetching data from the server and storing it in an Array. The ProductsComponent serves as the parent component, while the ProductsListComponent and ProductListItemsComponent are its children components. The flow ...

Ensuring Koa ctx.query is valid prior to invoking the handler function

I'm currently working on a basic route that looks like this: router.get('/twitter/tweets', async (ctx) => { const { limit, page, search } = ctx.query ctx.body = { tweets: await twitter.all(limit, page, search), } }) The issue I ...

Challenges with implementing Typescript in Next.js and the getStaticProps function

Having trouble with the implementation of getStaticProps where the result can be null or some data. The typings in getStaticProps are causing issues, even after trying conditional props. Any suggestions? type ProductType = { props: | { ...

The webpage is currently experiencing difficulty in displaying any information

As a beginner in React and typescript, I am working on a simple application that fetches data from an API and displays it on a web page. Despite fixing some errors and seeing the data in the console log, I am struggling to display any data on the actual we ...

Unit testing of an expired JWT token fails due to the incorrect setting of the "options.expiresIn" parameter, as the payload already contains an "exp" property

I am having trouble generating an expired JWT token for testing purposes and need some guidance on how to approach it. How do you handle expiration times in unit tests? This is what I have attempted so far : it('should return a new token if expired& ...

A guide on altering the color of a badge through programming

I am curious to learn how I can dynamically change the color of a badge in Angular. My goal is to initially set the color of the badge to white, and then if the percVLRiskTotal reaches a specific value, change the color to green as an example. CSS: <sp ...

Using React to iterate through the child components of the parent

I have created a component that can accept either a single child or multiple children. Here is an example with multiple children: <SideDataGridItem> <div id='top'> <div>A1</div> <div>B1</div> ...

Highcharts - Customize Pie Chart Colors for Every Slice

I'm working on an angular app that includes highcharts. Specifically, I am dealing with a pie chart where each slice needs to be colored based on a predefined list of colors. The challenge is that the pie chart is limited to 10 slices, and I need to a ...

ngOnChanges will not be triggered if a property is set directly

I utilized the modal feature from ng-bootstrap library Within my parent component, I utilized modalService to trigger the modal, and data was passed to the modal using componentInstance. In the modal component, I attempted to retrieve the sent data using ...

Guide to resolving the error "Type 'void' cannot be assigned to type 'Function' in VueJS"

I've created a Vue component that requires a function pointer to execute a delete action. <template> <q-card class="my-card" > <q-img :src="media.normal || media.original"> <div class="absolute ...

Using a try block inside another try block to handle various errors is a common practice in JavaScript

In an effort to efficiently debug my code and identify the location of errors, I have implemented a try-catch within a try block. Here is a snippet of the code: for (const searchUrl of savedSearchUrls) { console.log("here"); // function will get ...

Is there a way to define one type parameter directly and another type parameter implicitly?

I am currently utilizing a UI-library that offers an API for constructing tables with a structure similar to this: type Column<Record> = { keys: string | Array<string>; render: (prop: any, record: Record) => React.ReactNode; } The l ...

Filtering nested arrays in Angular by cross-referencing with a navigation menu

In the legacy application I'm working on, we have a navigation menu along with a list of user roles. Due to its legacy nature, we have accumulated a significant number of user roles over time. The main goal is to dynamically display the navigation me ...

Error in React Native Typescript: The type 'string' cannot be assigned to the type '"solid" | "clear" | "outline"'. (Error code: ts(2322))

I have integrated TypeScript with React Native in my project. import React from 'react'; import { Button } from 'react-native-elements'; import { IThemedButton } from '../../../models/themedButton'; interface IThemedButtonPr ...