Identifying Data Types in Typescript Using a Union Type Guard

I came across this interesting type guard:

enum X {
    A = "1"
}

const isNullableX = (value: any): value is X | null => false

let bar: string | null = ''

if (isNullableX(bar)) {
  console.log(bar)
}

Surprisingly, in the last console.log, the type of bar turns out to be just X instead of X | null. Any insights on how this behavior works? I noticed that when I change the type declaration of bar to let bar: unknown = '', the correct type is inferred in the console.log.

Feel free to experiment with this example using the TypeScript Playground: Playground

Answer №1

In TypeScript, control flow analysis is used to narrow down the apparent type of an expression and make it more specific. For instance, when a value is assigned to a variable with a union type, the compiler narrows down the variable's type to only those members of the union that are compatible with the assigned value until the next assignment is made. As shown in this example:

let foo: string | null = ''    
foo // string

After the assignment, the compiler has narrowed down `foo` from `string | null` to just `string` because it recognizes that `foo` cannot be `null`. However, if you change the assignment to something like `let foo = Math.random()<0.5 ? '' : null`, the narrowing won't occur as expected.


Control flow analysis can either narrow down the type of an expression or reset it back to its original type. It's not feasible to widen a type arbitrarily or mutate it into something unrelated.

When you invoke a user-defined type guard function like:

const isNullableX = (value: any): value is X | null => false

it will narrow down the input type based on the output. In the following call:

if (isNullableX(foo)) {
  foo // X
}

`foo` is narrowed down from `string` to something assignable to `X | null`. Since `null` cannot be assigned to `string`, the only possible result here is `X`, leading to the narrowing of `foo` to `X`. If you were expecting `foo` to change from `string` to `X | null`, that cannot happen without a reset followed by a narrowing, which isn't the case here since there is no reassignment involved.

Link to code on Playground

Answer №2

How do you feel about this solution?

enum Y {
    B = "2"
}

function ensureIsTrue(condition: unknown): asserts condition is boolean {
  if (!condition) {
    throw new Error("Condition not met!");
  }
}

const isNullableY = (value: any): value is Y | null => false

let bar = '' as string | null;

ensureIsTrue(isNullableY(bar));

console.log(bar);

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

Obtain the query response time/duration using react-query

Currently utilizing the useQuery function from react-query. I am interested in determining the duration between when the query was initiated and when it successfully completed. I have been unable to identify this information using the return type or para ...

Using ES6 proxy to intercept ES6 getter functions

I have implemented a proxy to track all property access on instances of a class, demonstrated in the code snippet below: class Person { public ageNow: number; public constructor(ageNow: number) { this.ageNow = ageNow; const proxy = new Proxy( ...

Encountering an error "Property is used before its initialization" in Angular 10 when attempting to input an object into a template

My code includes a class: import * as p5 from 'p5'; export class Snake{ constructor() { } sketch = (p: p5) => { p.setup = () => { ... } } } To instantiate this class in app.component, I do the follow ...

Combining the `vuex-typex` npm package's `dist/index.js` for optimal performance with Jest testing framework

I initially raised this question as an open issue on GitHub. My experience with Vue.js, Vuex, TypeScript, and vuex-typex has led me to encounter syntax errors during Jest testing. I am relatively new to working with Vue.js, TypeScript, and Jest. It is wo ...

Capture a screenshot with Puppeteer at a random URL stop

I am facing an issue with my service nodejs running on Ubuntu, where I use puppeteer to capture screenshots of pages. However, the method page.screenshot({fullPage: true, type: 'jpeg'}) sometimes fails on random URLs without displaying any errors ...

Tips on preventing image previews from consuming too much text data while updating a database in Angular 12 using Material UI for image uploads within a FormGroup object

Currently working with Angular 12 and Angular Material for image uploads with preview. I have a formgroup object below, but I'm running into issues with the 197kb image preview text being inserted into the database. Despite trying setValue/patchValue/ ...

Error message stating: "The 'MktoForms2' property is not recognized within the scope of 'Window & typeof globalThis'."

Encountering the following error message: (Property 'MktoForms2' does not exist on type 'Window & typeof globalThis') while working with react and typescript useEffect(() => { window.MktoForms2.loadForm("//app-sj11.marke ...

"Benefit from precise TypeScript error messages for maintaining a streamlined and organized architecture in your

As I dip my toes into the world of functional programming with typescript for a new project, I am encountering some challenges. In the provided code snippet, which follows a clean architecture model, TypeScript errors are popping up, but pinpointing their ...

Steps to automatically make jest mocked functions throw an error:

When using jest-mock-extended to create a mock like this: export interface SomeClient { someFunction(): number; someOtherFunction(): number; } const mockClient = mock<SomeClient>(); mockClient.someFunction.mockImplementation(() => 1); The d ...

Triggering an event in Angular 2 after ngFor loop completes

I am currently attempting to utilize a jQuery plugin that replaces the default scrollbar within dynamic elements in Angular 2. These elements are generated using an ngFor loop, making it impossible to directly attach jQuery events to them. At some point, ...

Steering clear of the generic Function type in React with TypeScript

Can anyone help me find a guideline that prohibits the use of "Function" as a type? myMethod: Function; I have searched but couldn't locate any information on this. Appreciate any suggestions :) ...

While working on a project that involves TypeScript, Material-UI, and Formik, I encountered an error that originated from

I recently downloaded a project from the following site: https://codesandbox.io/s/gn692 Upon encountering some errors that I couldn't resolve on my own, I decided to download this project to see how it's implemented. Surprisingly, it runs smoothl ...

Implementing handleRequest as an asynchronous function within the passportjs guard

@Injectable() export class RefreshAuthGuard extends JwtAuthGuard { constructor( private readonly jwtService: JwtService, ) { super(); } public handleRequest(err: any, user: any, info: Error, ctx: any): any { if (err ...

Overriding the 'first' attribute in PrimeNG's lazy table when implementing filtering

I encountered an issue while attempting to set up a primeNG table using query parameters. For example, when accessing , the data displayed should pertain to "Joe" and start at the 20th entry. To handle the large volume of data my backend can provide, lazy ...

Having trouble implementing the ternary operator (?) in TypeScript within a Next.js project

Trying to implement a ternary operator condition within my onClick event in a Next.js application. However, encountering an error indicating that the condition is not returning any value. Seeking assistance with resolving this issue... https://i.stack.img ...

What is the process for extracting Excel .xlsx information from a POST request body in an Express API?

I've created an Angular frontend application that sends an excel (.xlsx) file as form data in the request body to my Express endpoint. Take a look at the function from my Angular service file below: uploadOrder(workOrder: File) { const formData: For ...

Tips for effectively handling the auth middleware in react.js by utilizing LocalStorage

After making a call to the authentication API, the plan is to save the authentication token in LocalStorage and then redirect to a dashboard that requires token validation for entry. However, an issue arises where the authentication token isn't immedi ...

The TypeOrm many-to-one relationship with a multiple column join is giving an error: The column mentioned in the reference, <column name>, was not found in the entity <entity name>

I have an entity called A with a composite primary key, and another entity called B that has foreign keys referencing both columns of entity A. I am currently attempting to establish a many-to-one relationship between entity B (many) and entity A (one). U ...

Using TypeScript to set an HTMLElement in a web page

Currently in the process of transitioning my old JavaScript code to TypeScript. One of the components I have is a Table Of Contents JSX component that helps me navigate easily to specific headings. I had a function that calculated the total offset needed ...

Issue with react-native-svg ForeignObject missing in project (React Native with Expo using TypeScript)

I am working on a React Native project in Expo and have incorporated the expo TypeScript configuration. Using "expo install," I added react-native-svg version 9.13.3 to my project. However, every time I attempt to render the SVG using react-native-svg, I ...