Creating a unique type with a suffix of `px`

Understanding how to create a Position type class is clear:

class Position {
  x: number = 0;
  y: number = 0;
}

However, I now require the x and y values to be integers with the suffix of px, like this:

const position = {
  x: '1px',
  y: '2px'
}

How can I define a type similar to this in TypeScript?

Answer №1

One approach you can take is to define the following:

type HeightUnit = '%' | 'px' | 'em' | 'vh';
type HeightProp = `${number}${HeightUnit}`

This will yield the following results:

myHeight: HeightProp;
myHeight = '10px'  --- This is valid
myHeight = '10'  --- This will result in a compilation error

To learn more about Template Literal Types, check out Template Literal Types documentation

Answer №2

Unfortunately, it is not possible to achieve this in Typescript as it involves the use of dependent types, which are not supported in Typescript. The language that is well-known for having dependent types is Idris.

To address this issue, a potential solution could be implemented like so:

const VALID_XY_VAL = /^(-?\d+)px$/;

class Point {
  _x: number = 0;
  _y: number = 0;

  constructor(x: string, y: string) {
    this.x = x;
    this.y = y;
  }

  get x () {
    return `${this._x}px`;
  }

  get y () {
    return `${this._y}px`;
  }

  set x(xVal: string) {
    const num = xVal.match(VALID_XY_VAL);
    if (num === null) throw new Error('Invalid value');
    this._x = Number(num[1]);
  }

  set y(yVal: string) {
    const num = yVal.match(VALID_XY_VAL);
    if (num === null) throw new Error('Invalid value');
    this._y = Number(num[1]);
  }
}

const p = new Point('1px', '2px');
p.x; // '1px'
p.y; // '2px'
p.x = '5px'; 
p.y = '5ac3px'; // Error.

This approach securely stores pixel values internally as numbers and enforces type-safety by throwing errors if an invalid value is set.

Regular Expression Online Tester

UPDATE

An alternative suggestion from Altocumulus proposes that the regex should be defined as a read-only static member within the Point class. Each method has its pros and cons: using a static member adheres more closely to the principle of least privilege but might restrict reusability. Ultimately, choosing a declared-const module-level immutable value may suffice, yet individual preferences may vary.

class Point {
  private static readonly VALID_VALUE: RegExp = /.../

Answer №3

Revising the Answer

While some may disagree with this answer, it is important to acknowledge that as developers, we play a crucial role in structuring our data. The question posed here is very specific, asking how to model the "{number}px" type in TypeScript. The truth is, there is only one solution - it is not feasible. However, the misunderstanding lies in the fact that the questioner believes this type of format is the answer to their problem. In reality, the challenge is about representing a 2D point with x and y values along with a unit. This can easily be accomplished using a simple structure like [number, number, 'px'], indicating two numeric values and a static 'px' unit. This approach offers flexibility and type safety. Resorting to regular expressions or complex runtime validations through classes might seem like solutions but do not contribute during compilation.

Original Response:

Do you really need a class to represent a pair? Using a class for such a basic construct is like using a sledgehammer to crack a nut. What you truly require is a simple pair + unit representation, which can be achieved through a tuple [a, b, unit] or a record {a: a, b: b, unit: unit}. As pointed out by others, defining a type with a number followed by 'px' suffix is not possible, but there are several alternative modeling approaches. Here are a few suggestions:


1. Proposal One - Modeling with Additional Unit Information

// represented as a triple tuple
type Point = [number, number, string]
const point = [1, 2, 'px']

// represented as a key-value map
type Point = {x: number, y: number, unit: string}
const point = {x: 1, y: 2, unit: 'px'}

2. Model with Strict Unit Type. If a strict unit like 'px' is certain, it can be defined in the type. I will only show examples using key-value map types to avoid repetition.

type Point = {x: number, y: number, unit: 'px'} // unit property must always be 'px'
const point = {x: 1, y: 2, unit: 'px'}

We can also create a constructor for points to eliminate manual entry of 'px':

const createPoint = (x: number, y: number): Point => ({x, y, unit: 'px'});
const point = createPoint(1, 2) // {x: 1, y: 2, unit: 'px'}

3. Modeling as a Pair of Strings but with Constructor While leaving the type as a pair of strings, we can generate this using a number constructor.

type Point = {x: string, y: string}
const createPoint = (x: number, y: number): Point => ({x: x + 'px', y: y + 'px'});
const point = createPoint(1, 2) // {x: '1px', y: '2px'}

4. Modeling as an Object with Special Get Functions

type Point = {values: () => {x: number, y: number}, pixels: () => {x: string, y: string}}
// utilizing closure to store arguments of createPoint function
const createPoint = (x: number, y: number): Point => ({
  values: () => ({x, y}),
  pixels: () => ({x: x + 'px', y: y + 'px'})
})
const point = createPoint(1, 2);
point.values() // {x: 1, y: 2}
point.pixels() // {x: '1px', y: '2px'}

Answer №4

When creating a type with x and y properties, you have two options. You can follow the example you provided, or you could create a different type that allows for both numbers and strings.

class Location {
  x: number | string = 0;
  y: number | string = 0;
}

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

Deliver the commitment to the data source connection settings in TypeORM

Is it possible to retrieve the datasource connection options from AWS Parameter Store instead of storing them as environment variables in a general JavaScript question? I am having difficulty finding a solution and seeking expert advice on this matter. Th ...

Import JSON data into Angular 2 Component

After much effort, I have finally figured out how to load JSON data into an Angular 2 Component. datoer.service.ts: import { Injectable } from '@angular/core'; import { Http, Response } from '@angular/http'; import { Observable } from ...

Angular2 checkboxes for filtering data

I'm working with an Angular2 grid that showcases an array of Fabrics, each with its own color or fabric type properties. Right now, all Fabrics are displayed in the grid, but I need to implement a series of checkboxes for color and fabric type, along ...

Utilizing Lazy Loading Modules within an Angular 2 (v5) App

I'm struggling to implement lazy loading in my Angular 2 (version 5.1.3) project. While following Todd Motto's guide on Lazy Loading Code Splitting, I am hitting a roadblock in getting it to function correctly. My app consists of multiple modul ...

The TypeScript compiler is generating node_modules and type declaration files in opposition to the guidelines outlined in the tsconfig.json file

For the past week, I've been trying to troubleshoot this issue and it has me completely puzzled. What's even more puzzling is that this app was compiling perfectly fine for months until this problem occurred seemingly out of nowhere without any c ...

Navigating through the keys of a parameter that can assume one of three distinct interfaces in TypeScript: a guide

Here is a function example: function myFunc(input: A | B | C) { let key: keyof A | keyof B | keyof C; for(key in input) { let temp = input[key]; console.log(temp); } } The definitions for A, B, and C are as follows: interfa ...

Having difficulty storing duplicate requests that are crucial for various services/components

Currently, I am tackling a project that involves displaying multiple sets of data to the user. Each set requires several requests to be made to the backend. Specifically, for the UserDetails dataset, I must query the getUser and getSigns endpoints. However ...

remove a specific element from an array

Hey there! I'm attempting to remove only the keys from an array. Here's the initial array: {everyone: "everyone", random: "random", fast response time: "fast response time", less conversations: "less conversatio ...

Cannot find property in type, and the parameter is implicitly of an unspecified type

I've been encountering this issue where I keep getting an error message. I attempted to resolve it by setting "noImplicitAny": false in tsconfig.json, but unfortunately that did not work. As for the 'Property does not exist on type' error, I ...

Guide on linking an Angular2+ app with an external API

Can anyone provide guidance on how to integrate an external API with authentication (username and password) into an Angular Application? I am comfortable connecting to APIs that don't require authentication, but I am facing difficulties with APIs that ...

Angular5+ Error: Unable to retrieve summary for RouterOutlet directive due to illegal state

When attempting to build my Angular App using ng build --prod --aot, I consistently encounter the following error: ERROR in : Illegal state: Could not load the summary for directive RouterOutlet in C:/Path-To-Project/node_modules/@angular/Router/router.d. ...

What is the process of converting the Object type returned from an Observable to an array of objects in Angular?

When utilizing the GET method to retrieve information, I have encountered a problem. Here is the code snippet: constructor(private http: HttpClient) { } items: Item[]; stuff: any[]; ngOnInit() { const url = ...; this.http.get(url) .subscribe(nex ...

Using Angular to make an HTTP POST request to fetch data

My trusty .net backpack has been working flawlessly. However, I encountered an issue when trying to connect it with the Angular front end. All backend requests are post requests and require passing an ApiKey in the body of each request. Interestingly, ever ...

One-of-a-kind npm module for typescript

As part of my project, I am enhancing an existing library to make it compatible with TypeScript. To showcase this modification, I have condensed it into a succinct Minimal working example The specified requirements To ensure backward compatibility, the li ...

How to enable Autocomplete popper to expand beyond the menu boundaries in Material UI

Within my Menu component, I have an Autocomplete element. When the Autocomplete is clicked, the dropdown list/Popper appears but it's confined within the Menu parent. How can I make it so that the Autocomplete's dropdown list/Popper isn't re ...

What is the best way to ensure that GCM push notifications are still received even when the app is closed or the

Currently, I'm in the process of developing an application using Ionic 2 that requires push notifications to be received. In certain scenarios, such as when the app is force-stopped on Android or when the device is turned off, push notifications are ...

What is the best way to utilize "exports" in package.json for TypeScript and nested submodules?

Looking to leverage the relatively new "exports" functionality in Node.js/package.json for the following setup: "exports": { ".": "./dist/index.js", "./foo": "./dist/path/to/foo.js" } so that ...

Error encountered: The database is not found during the migration creation process in MikroORM

Attempting to create migrations with mikroORM has been a challenge as I am unable to generate the table itself. The error message indicating that the database "crm" does not exist leaves me puzzled about what steps I may have missed. Here is the code snip ...

Describe the TypeScript type for an object with constant keys

My query resembles the one found in this Typescript interface definition question, but has a slight variation. I am beginning with an object where the keys are constants: const KEYS = { KEY1: 'hello', KEY2: 'world' } as const; How ...

Assign a value to a file input in Angular 6

I'm currently working with Angular 6 and I have a requirement to retrieve an image that is dropped into a div element and assign it as the value of an input type="file" within a form. The process involves the user dropping an image into the designate ...