Getting the Final Character from a TypeScript String Constant

Can we extract the final character from a string without using the entire alphabet as an enum? Right now, I'm focusing on numeric digits.

I'm somewhat puzzled about why my current approach isn't yielding the correct results.

type Digit = '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9';

type GetFirstDigitOfStringLiteralNumber<T extends `${string}${Digit}`> = T extends `${infer D extends Digit}${string}` ? D : never;
type GetLastDigitOfStringLiteralNumber<T extends `${string}${Digit}`> = T extends `${string}${infer D extends Digit}` ? D : never;

// This is accurate
type FirstCharacter = GetFirstDigitOfStringLiteralNumber<"21222">;
//    ^? "2"
// This is accurate
type LastCharacter1 = GetLastDigitOfStringLiteralNumber<"12">;
//    ^? "2"
// This is incorrect
type LastCharacter2 = GetLastDigitOfStringLiteralNumber<"21222">;
//    ^? '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9'

Playground

The issue with `LastChracter2` not functioning correctly surprises me. My assumption is that TypeScript struggles to identify the `${string}` portion accurately and thus stops analyzing static data due to this limitation.

However, it remains unclear why the system doesn't default to returning `never` when unable to match the `Digit` portion to a specific character.

Edit:

Assigning a name to the inferred `${string}` section appears to produce the `never` outcome I originally anticipated, even though no other changes were made:


type Digit = '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9';

type GetLastDigitOfStringLiteralNumber<T extends `${string}${Digit}`> = T extends `${infer L extends string}${infer D extends Digit}` ? D : never;

// This works fine
type InferLeading = GetLastDigitOfStringLiteralNumber<"12">;
//    ^? "2"

// Why does this fail?
type InferLeading2 = GetLastDigitOfStringLiteralNumber<"112">;
//      ^? never

Answer №1

The issue at hand arises from using ${infer L}${infer R} on a string that exceeds two characters in length. In such cases, the inference will designate the first character as L and the rest as R. For example, with the input "123456", L would be "1" and R would be "23456". This approach leads to incorrect results when checking if R extends Digit where R is more than one character.

To rectify this issue, one can introduce recursion into the type declaration.

type GetLastDigitOfStringLiteralNumber<T extends `${string}${Digit}`> = 
  T extends `${string}${infer REST extends `${infer L}${Digit}`}` 
    ? GetLastDigitOfStringLiteralNumber<REST> 
    : T

Playground

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

Encountering an Uncaught Error: MyModule type lacks the 'ɵmod' property

I am currently working on developing a custom module to store all my UI components. It is essential that this module is compatible with Angular 10 and above. Here is the package.json file for my library: { "name": "myLibModule", &qu ...

Refresh a doughnut chart in real-time using NG2Charts

Currently, I am in the process of developing a macronutrient calculator as part of a project. The idea is to have a form where users can input values, and a corresponding doughnut chart will display with initial values set at 0. However, upon clicking the ...

Steer clear from using the implicit 'any' type while utilizing Object.keys in Typescript

I have a unique situation where I need to loop over an Object while maintaining their type without encountering the error "Element implicitly has an 'any' type because 'ContactList' has no index signature". Despite extensive discussion ...

Is it possible for moduleNameMapper to exclude imports made by a module located within node_modules directory?

I am trying to figure out how to make my moduleNameMapper ignore imports from the node_modules directory. The issue is that one of the dependencies, @sendgrid/mail, uses imports starting with ./src/ which causes problems when importing into Jest. My curre ...

Issue with Angular project: View not being updated when using behaviorSubjects

Within my Angular project, I am retrieving data from an API using a service and storing the data within a BehaviorSubject as shown below private categorySubject = new BehaviorSubject<number | null>(null); apiBehavior = new ReplaySubject<ApiRes ...

Is there a way to showcase just the month in one spot when presenting data using Angular 13?

Just starting out with Angular and facing a challenge in the Milestone section. There is a loop for displaying years and months, but I need to ensure that the month name is displayed only once for each specific year. Can someone provide me with a possible ...

ERROR: The use of @xenova/transformers for importing requires ESM

When working with my Node.js application, I usually start by importing the necessary modules like this: import { AutoModel, AutoTokenizer } from '@xenova/transformers'; Afterwards, I utilize them in my code as shown below: const tokenizer = awai ...

Preserve the timestamp of when the radio query was chosen

I'm interested in finding a way to save the user's selected answer for a radio button question and track the time they saved it. Is there a way to achieve this using HTML alone? Or would I need to utilize another coding language or package? Just ...

Transforming a detailed JSON structure into a more simplified format with Angular 2

As a newcomer to Angular 2, I find myself encountering a hurdle in filtering unnecessary data from a JSON object that is retrieved from a REST API. Below is an example of the JSON data I am working with: { "refDataId":{ "rdk":1, "refDataTy ...

Cross-component communication in Angular

I'm currently developing a web-based application using angular version 6. Within my application, there is a component that contains another component as its child. In the parent component, there is a specific function that I would like to invoke when ...

Why does Typescript not enforce a specific return type for my function?

In my custom Factory function, I need to return a specific type: type Factory<T> = () => T; interface Widget { creationTime: number; } const createWidget: Factory<Widget> = () => { return { creationTime: Date.now(), foo: &a ...

What is the correct way to declare a class as global in TypeScript?

To prevent duplication of the class interface in the global scope, I aim to find a solution that avoids redundancy. The following code snippet is not functioning as intended: lib.ts export {} declare global { var A: TA } type TA = typeof A class A { ...

Tips for resolving the "trim" of undefined property error in Node.js

Looking to set up a basic WebAPI using Firebase cloud functions with express and TypeScript. Here's the code I have so far: import * as functions from 'firebase-functions'; import * as express from 'express'; const app = express( ...

Steps for transferring an uploaded .CSV file to a Web service

I'm exploring the process of sending a file uploaded from the UI (angular) to a .NET web service in order for it to parse a CSV file and create a list of objects. My current understanding of the logic flow is: File upload ---> Web Service (parse fil ...

Verify the occurrence of an element within an array inside of another array

Here is the scenario: const arr1 = [{id: 1},{id: 2}] const arr2 = [{id: 1},{id: 4},{id: 3}] I need to determine if elements in arr2 are present in arr1 or vice versa. This comparison needs to be done for each element in the array. The expected output sho ...

Determining the type of a keyof T in Typescript

I am currently working on developing a reusable function that takes an observable and applies various operators to return another observable. One issue I am facing is getting the correct type for the value based on the use of the key. The code snippet bel ...

How come TypeScript does not detect when a constant is used prior to being assigned?

There's an interesting scenario I came across where TypeScript (3.5.1) seems to approve of the code, but it throws an error as soon as it is executed. It appears that the root cause lies in the fact that value is being declared without being initiali ...

What is the process for inputting a predefined function into an interface?

In my project, I have a Locale interface that defines the properties of a locale for my component: interface Locale { src: string; alt: string; language: string; i18nFormat: string; } During debugging, I am using the built-in .toSource() function ...

SvgIcon is not a recognized element within the JSX syntax

Encountering a frustrating TypeScript error in an Electron React App, using MUI and MUI Icons. Although it's not halting the build process, I'm determined to resolve it as it's causing issues with defining props for icons. In a previous pro ...

Discovering the origins of the node.js native modules and delving into the intricacies of typed modules

I am using a Windows machine and trying to locate where node fetches the source code for native modules. On my system, I can only find the @types file which contains "Typed Only" modules. For example, the module "assert" is available in the master/lib fold ...