Stop const expressions from being widened by type annotation

Is there a way to maintain a constant literal expression (with const assertion) while still enforcing type checking against a specific type to prevent missing or excess properties?

In simpler terms, how can the type annotation be prevented from overriding the as const assertion and widening the type?

I have a basic understanding of what's happening and I've asked around on chat, but it seems like there may not be a straightforward solution in the realm of types. Perhaps there's a clever workaround that I'm not aware of.

Here's the scenario:

I need to create a config object that allows me to infer types conditionally based on its values, all while ensuring that the config precisely matches the keys of a specified type.

Consider this minimal example. The State type defines the key structure, and the config object must align with those keys for type validation. However, I also need the config to remain constant so I can access a union type at a unit level, instead of a widened string type.

playground

type State = Readonly<{
    a: number;
    b: string;
}>;

const config: Record<keyof State, { value: string }> = {
    a: { value: "aaa" },
    b: { value: "bbb" }
} as const;

// Desired output should be "aaa" | "bbb"
type ConfigValues = (typeof config)[keyof typeof config]["value"];

One potential solution is as follows:

const config = {
    a: { value: "aaa" },
    b: { value: "bbb" }
} as const;

// eslint-disable-next-line @typescript-eslint/no-unused-vars
const config_check: Record<keyof State, any> = config;

However, this approach only checks for missing properties, not excessive ones :/.

Keep in mind that this illustration is quite simplistic. In reality, the config is more intricate, and the desired inference type relies on conditional logic tied to the values.

Furthermore, encountering this issue repeatedly suggests it's not just an isolated incident.

Answer №1

One way to keep a type narrow for inference is by using a generic helper function like this:

function createConfig<P extends string, T extends Record<keyof State, { value: P }>>(
    cfg: { [K in keyof T]: K extends keyof State ? T[K] : never }) {
    return cfg
}
const config = createConfig({
    a: { value: "aaa" },
    b: { value: "bbb" },
    c: { value: "ccc" } // may cause conditional type error similar to excess property check
})

type keys = (typeof config)[keyof typeof config]["value"]; // "aaa" | "bbb"
type keyA = (typeof config)["a"]["value"] // "aaa"

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

Using Selenium in JavaScript to upload an image is a straightforward process

I am trying to automate the process of uploading a picture using Selenium with the following script: driver.findElement(By.id(`avatar-upload`)).sendKeys(`/home/user/Desktop/smg935-0hero-0930.jpeg`) But I keep receiving this error: ElementNotInteractable ...

Ways to conduct testing on React Native Typescript app COMPONENTS using jest

I've been struggling to set up testing for my React Native Typescript Components using Jest. Despite searching through various examples and solutions (such as this one, that one, another link, etc.), I still can't seem to get it working. Even fol ...

Using React.ReactNode as an argument in Storybook

This is a unique button component type that I have created import React from 'react' export type ButtonProps = { label: string; color?:'primary' | 'secondary' | 'tertiary'; size?:'mobile' | 'tabl ...

What is the best way to convert a string to JSON format using Javascript?

I've been working on retrieving the results of a PHP query into a JavaScript object. However, I encountered an error message in the console saying Uncaught SyntaxError: Unexpected token { in JSON at position 66. I understand that this error occurs bec ...

Transfer a Sencha Touch application from iOS to Android and Windows devices

I am a dedicated Android developer who is diving into the world of Sencha Touch, Windows app development, and VisualStudio environments for the first time. Please excuse the detailed explanation of my problem, as I want to make sure all crucial details are ...

Create a function that will repeatedly call itself at specified intervals, increment a variable, and update the class values of elements with an id matching the current value of the

I'm attempting to create a setup that cycles through different images <div id="img_box" class="shadow" onload="imgCycle(1)";> <img id="1" class='opaque imgbox selected' src="media/img ...

Typescript enhances the functionality of the Express Request body

I need help with the following code snippet: const fff = async (req: express.Request, res: express.Response): Promise<void> => {...} How can I specify that req.body.xxx exists? I want it to be recognized when I reference req.body.xxx as a propert ...

Cannot upload the same file to both Spring and Angular twice

Having trouble uploading the same file twice. However, it works fine when uploading different files. Encountering an error under the Network tab in Chrome { timeStamp: ......, status: 417 error: 'Bad Request', message: 'Required reques ...

The command '.' is unable to be executed as an internal or external command, executable program, or batch file when using npm start -- -e=stag -c=it

After executing the command shown below npm start -- -e=stag -c=it An error is generated: ./scripts/start.js -e=stag -c=it '.' is not recognized as an internal or external command, operable program or batch file. What can be done to resolve th ...

Explore the associative array within a JSON using jQuery to extract and manipulate the values within the array

I'm working with a JSON file containing surnames and first names in an array, along with other objects. How can I specifically extract the names "Jhon" and "Jason"? Below is a snippet from my JSON file: [{ "surname": "Vlad", "first_name": [ ...

What are the best methods for concealing a ng-repeat element with AngularJS?

.controller('CouponsCtrl', ['$scope', '$http', '$window', '$location', '$ionicPopup','$ionicLoading', function($scope, $http, $window, $location, $ionicPopup,$ionicLoading) { ...

Creating a custom progress bar using Javascript and Jquery

I developed a progress bar that is fully functional. Here is the HTML structure: <div class="progress"> <div class="progress-bar progress-bar-striped active" role="progressbar" aria-valuenow="40" aria-valuemin="0" aria-valuemax="100" style ...

Dealing With HttpClient and Asynchronous Functionality in Angular

I've been pondering this issue all day. I have a button that should withdraw a student from a class, which is straightforward. However, it should also check the database for a waiting list for that class and enroll the next person if there is any. In ...

The challenge of optimizing Angular websites for SEO while also addressing page reload issues

I am struggling to create SEO-friendly URLs in my Node server backend. I want URLs like the following: http://localhost:3003/my-new-mobile //product http://localhost:3003/my-new-category //category http://localhost:3003/my-first-blog-post // blog post I ...

After mapping in JavaScript, delete the property name

Is there a way to exclude the property name from the returned value? I want to eliminate the property name "projects: [ ]" from the output. router.get("/", (req, res, next) => { Project.find() .exec() .then(docs => { res.status(200) ...

Using Nest JS to Handle Null Responses in Services

When using Nest Js to call Axios and get data from the Facebook API, I encountered a problem where the service was returning a null value. Strangely, when I tried calling the response using console.log, it did return a value. Any suggestions on what I migh ...

Unable to load local image file for texture in Three.js

When attempting to utilize a local image for loadTexture in Three.js, I encountered the following error: Uncaught SecurityError: Failed to execute 'texImage2D' on 'WebGLRenderingContext': The cross-origin image at .... may not be loade ...

Having trouble with @viewChild not activating the modal popup and displaying an error message stating that triggerModal is not a function in

I am facing an issue where the click event is not being triggered from the parent component to display a Bootstrap 3 modal. I am getting the following error: ERROR TypeError: this.target.triggerModal is not a function This is what I have done so far: Pa ...

Tips for avoiding cursor sticking in css rotate transform in firefox?

I have a unique challenge in this code where I want the user to grab the black square and rotate it around the inner circle. Check out the code snippet here. While attempting to rotate the square, you might notice that the cursor sometimes gets stuck in ...

JavaScript Firebase: Service worker malfunctioning during navigation

I'm currently developing a website that relies on firebase messaging. To make this happen, a specialized service worker for firebase has been integrated into the site. This website functions as a webchat platform where messages are synchronized and s ...