Tips for validating nominal-typed identifiers

I recently started experimenting with the enum-based nominal typing technique explained in more detail at this link.

enum PersonIdBrand {}
export type PersonId = PersonIdBrand & string

interface Person {
  id: PersonId
  firstName: string
  lastName: string
}

During my testing, I encountered an issue while adding types to certain factory methods utilized in tests. These utility functions facilitate the creation of test data with default values that can be overridden selectively:

const makeTestPerson = ({
  id = 'personId' as PersonId,
  firstName = 'Bob',
  lastName = 'Smith'
}: Partial<Person> = {}): Person => ({
  id,
  firstName,
  lastName
})

const person = makeTestPerson({ lastName: 'Ross' })

However, when using tsc, an error occurs:

error TS2322: Type 'PersonId' is not assignable to type 'never'.

11   id = 'personId' as PersonId,

If I switch to using id: string instead, it compiles without any issues. Is there a way to ensure these functions pass type checking with PersonId?


Update: After further exploration, I believe there might be a more underlying problem with this approach:

const maybePersonId: PersonId | undefined = ("personId" as PersonId)

This also results in a failure message:

TS2322: Type 'PersonId' is not assignable to type 'undefined'.

But why does this fail? Shouldn't an X always be able to be assigned to X | undefined?

Answer №1

It seems that recent changes in TypeScript have altered the way it handles unions and intersections with empty types. Unfortunately, I couldn't locate the specific PR that initiated this change, but it seems to have occurred around version 2.9.

The compiler team at Microsoft utilizes an intersection with a type containing one extra member for branded types, rather than with an enum:

export type Path = string & { __pathBrand: any };

Following their approach, consider defining branded types like so:

export type PersonId = { __personIdBran: any } & string

interface Person {
  id: PersonId
  firstName: string
  lastName: string
}

const makeTestPerson = ({
  id = 'personId' as PersonId,
  firstName = 'Bob',
  lastName = 'Smith'
}: Partial<Person> = {}): Person => ({
  id,
  firstName,
  lastName
})

const person = makeTestPerson({ lastName: 'Ross' })

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

The successful operation of 'Ionic serve --lab' may involve the need to manually save the app.module

We are currently working on an Ionic project that consists of several pages and a single custom provider named request.ts. The issue we are facing is that whenever we launch it using the command ionic serve --lab, the compilation fails (with the error poin ...

What is the best way to categorize an array based on a specific key, while also compiling distinct property values into a list

Suppose there is an array containing objects of type User[]: type User = { id: string; name: string; role: string; }; There may be several users in this array with the same id but different role (for example, admin or editor). The goal is to conv ...

Tips for effectively combining the map and find functions in Typescript

I am attempting to generate an array of strings with a length greater than zero. let sampleArray2:string[] = ["hello","world","angular","typescript"]; let subArray:string[] = sampleArray2 .map(() => sampleArray2 .find(val => val.length & ...

The user interface is not being refreshed in the select box after removing control from the reactive form

Within my project, I am utilizing "@angular/cli": "1.2.6", "@angular/core": "^4.0.0" Objective My goal is to create a dynamic form for a product that includes feature inputs. When the user clicks the "add feature" button, a new feature column with a sel ...

Is there a way for me to retrieve the header values of a table when I click on a cell?

I have a project where I am developing an application for booking rooms using Angular 2. One of the requirements is to be able to select a cell in a table and retrieve the values of the vertical and horizontal headers, such as "Room 1" and "9:00". The data ...

The deletion by index feature seems to be malfunctioning in Angular

Is there a way to delete an item by its ID instead of just deleting the last element using this code? I want to create input fields with a delete button next to each one simultaneously. TS: public inputs: boolean[] = []; public addNew(): void { this ...

Is it possible to use Angular signals instead of rxJS operators to handle API calls and responses effectively?

Is it feasible to substitute pipe, map, and observable from rxjs operators with Angular signals while efficiently managing API calls and their responses as needed? I attempted to manage API call responses using signals but did not receive quick response t ...

What are the limitations of using concatMap for handling multiple requests simultaneously?

In my current function, I am receiving an array of objects called data/ids as a parameter. Within this function, I need to execute a post request for each element/id: fillProfile(users) { const requests = []; console.log( 'USERS.length:&apos ...

How to capture a screenshot of the current screen using Nativescript programmatically

After taking a screenshot in a NativeScript app, is there a way to display a popup asking if the user wants to save the picture? I attempted using the 'nativescript-screenshot' plugin, but it only copies elements within the application: nat ...

While working on a project in React, I successfully implemented an async function to fetch data from an API. However, upon returning the data, I encountered an issue where it was displaying as a

I am working with React and TypeScript and have the following code snippet: const fetchData = async () => { const res: any = await fetch("https://api.spotify.com/v1/search?q=thoughtsofadyingatheist&type=track&limit=30", { met ...

Require assistance in accurately assigning a date to a Date[] in Typescript Array without altering current elements

In my current code, I have a loop that verifies if a date is a holiday and then adds it to an array. The issue I'm facing is that whenever I assign a new element to the array, all previous data in the list gets updated to the latest element. Here&apos ...

Issue with dependencies resolution in Nest framework

While delving into NestJS dependencies, I encountered an issue. As a beginner in learning Nest, I am still trying to grasp the correct way to structure everything. The problem lies in Nest's inability to resolve dependencies of the ChatGateway. It&a ...

Tips for incorporating SectionList sections in React Native using an array

I am working with an array of objects named movies (const movies = movie[]). Each movie object includes properties like name, description, date and duration. movie: { name: string; description: string; date: Date; duration: number } My ...

utilize the useRef hook to display the total number of characters within a text area

Introducing the following component: import React from 'react'; export interface TexareaProps extends React.TextareaHTMLAttributes<HTMLTextAreaElement> { maxLength?: number; id: string; } export const Textarea = React.forwardRef( ( ...

The implementation of race in React Redux Saga is proving to have negligible impact

I have implemented the following saga effect: function* loginSaga() { const logoutTimeoutCreationDate: string | null = yield localStorage.getItem('logoutTimeoutCreationDate'); let logoutTimeout: number; if (!logoutTimeoutCreationDate || + ...

Elevate the scope analysis for a function within the Jasmine framework

I have written a few functions within the app component. I am experiencing an issue with increasing coverage in the summary for these component methods. The test cases are functioning correctly, but some lines are not being accounted for in the coverage s ...

Navigating the NextJS App Directory: Tips for Sending Middleware Data to a page.tsx File

These are the repositories linked to this question. Client - https://github.com/Phillip-England/plank-steady Server - https://github.com/Phillip-England/squid-tank Firstly, thank you for taking the time. Your help is much appreciated. Here's what I ...

How to effectively send an HTTP GET request to a REST API in Angular 2 and save the response in a JSON object

Currently, I am attempting to execute a GET request to the GitHub API using Angular2. The returned data is in JSON format, and my goal is to store it in a JSON object for further processing. While referring to the Angular2 documentation for guidance, I en ...

BaQend is all set to go as it initializes, just make sure to verify the user's login

Currently, I am utilizing BaQend and Ionic2 to implement certain tasks at the start of my application. 1. Database Readiness Instead of repeating this process on every page: ionViewCanEnter(): Promise<baqend> { // Verify Baqend SDK readine ...

One issue that may arise is when attempting to use ngOnDestroy in Angular components while rearranging user transitions

Encountered an issue recently with Angular - when the user navigates from component A to component B, component A remains active unless ngOnDestroy is triggered. However, if the user visits component B before going to component A and then leaves, ngOnDes ...