A TypeScript Record that allows for inferable value types

How can I construct a map that enforces the presence of all keys while still allowing the inference of the types of its values?

If I have certain keys, for example:

type State = "OPEN" | "CLOSED";

Method 1: using an untyped object

const stateToNaturalLangMap = {
  OPEN: "open",
  CLOSED: "closed"
} as const;

// "OPEN" | "CLOSED"
type KeysOfMap = keyof typeof stateToNaturalLangMap;

// "open" | "closed"
type ValuesOfMap = (typeof stateToNaturalLangMap)[keyof typeof stateToNaturalLangMap];

Method 1 allows for the inference of key and value types, but it does not enforce the inclusion of all keys in the map (or the addition of extra keys).

Method 2: using Record

const stateToNaturalLangRecord: Record<State, string> = {
  OPEN: "open",
  CLOSED: "closed"
} as const;

// "OPEN" | "CLOSED"
type KeysOfRecord = keyof typeof stateToNaturalLangRecord;

// string
type ValuesOfRecord = (typeof stateToNaturalLangRecord)[keyof typeof stateToNaturalLangRecord];

Method 2, on the other hand, ensures that all keys are part of the map (and prevents the addition of other keys), but it does not allow the inference of value types (the type of values is string).

TS Playground Link

Answer №1

Utilize the satisfies operator for the following purposes:

  • Maintain the exact type of your constant
  • Ensure it adheres to the shape of Record<State, string>
type State = "OPEN" | "CLOSED";

const stateToNaturalLangRecord = {
  OPEN: "open",
  CLOSED: "closed"
} as const satisfies Record<State, string>;

// "OPEN" | "CLOSED"
type KeysOfRecord = keyof typeof stateToNaturalLangRecord;

type ValuesOfRecord = (typeof stateToNaturalLangRecord)[keyof typeof stateToNaturalLangRecord];
// type ValuesOfRecord = "open" | "closed"

TS 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

What is the best way to forward all URLs to one central page?

I've recently started working with Angular and I'm currently developing a web app project using Angular 9. I could really use your help with this. My goal is to have a dynamic URL structure for the web app, such as https://www.myMainURL.com/***, ...

Switching over to Typescript: Sending mapped properties

I'm having trouble converting my React.JS script to TypeScript. I need assistance in creating a drop-down navigation bar on my website. Here's a snippet from my Header.tsx file: The error message Property 'onClick' does not exist on t ...

Is the validity of the expression !args.value || args.value.length true?

After analyzing this segment of code, I noticed an interesting expression: !args.value || args.value.length For instance, consider the following scenario: let v = {}; console.log(!v.value); //outputs true console.log(v.value); //outputs undefined con ...

The requested resource could not be located at @angular/platform-browser.js

I am attempting to set up ASP.NET MVC 5 (not Core) + Angular 2.0.0 + JSPM + SystemJS + TS Loader. Upon running the application, I encounter the following error: Failed to load resource: the server responded with a status of 404 (Not Found) http://localho ...

Issue with Typescript typing for the onChange event

I defined my state as shown below: const [updatedStep, updateStepObj] = useState( panel === 'add' ? new Step() : { ...selectedStep } ); Additionally, I have elements like: <TextField ...

What is the proper way to create a React Context in TypeScript that accepts both a ref and a setState function as arguments?

When encountering various errors, one of them being: Type 'Dispatch<SetStateAction<null>>' is not assignable to type '() => void' My code looks something like this: import React, { ReactElement, ReactNode, useEffec ...

TS7016: No declaration file was found for the module named 'rxjs'

I recently updated my Angular App dependencies and successfully installed them. However, I am now facing an issue with 'rxjs'. The IDE returned the following error: TS7016: Could not find a declaration file for module 'rxjs'.'C:/ ...

What is the process for incorporating buttons into an Angular mat-table?

I have successfully utilized Angular mat-table to showcase data retrieved from a database: view the image description here <table mat-table [dataSource]="UserDataSourceFilters" class="mat-elevation-z1 mt-5"> <ng-co ...

Is it possible to retrieve cached data from React Query / Tan Stack Query outside of the React tree?

Currently, I am in the process of developing an error handler for our mobile app that will send user data to the server when unexpected errors occur. Previously, I was able to easily retrieve user information from a global state. However, after removing t ...

Despite using Enzyme to locate a component again, the expected prop value is still not being returned

Two components are involved here - a modal and a button that is meant to open the modal by setting its isOpen prop to true. The initial state of the modal is set to false, but when the button is clicked, it should change to true. While everything works fi ...

What causes TypeScript to malfunction when using spread in components?

What is the reason for typescript breaking when props are sent to a component using a spread statement? Here's an example code snippet: type SomeComponentProps = Readonly<{ someTitle: string }> const SomeComponent = ({ someTitle }: SomeCompo ...

How can you create a unique record by appending a number in Javascript?

Currently, when a file already exists, I add a timestamp prefix to the filename to ensure it is unique. However, instead of using timestamps, I would like to use an ordinal suffix or simply append a number to the filename. I am considering adding an incr ...

When I attempt to add a todo item by clicking, the Url value is displayed as "undefined"

I am facing an issue with my household app where, upon clicking the button to navigate to the addtodo page, the URL specific to the user's house is getting lost. This results in the todolist being stored as undefined on Firebase instead of under the c ...

Guide to setting a dynamic value for an input List property in Angular

How can I render multiple dropdowns in Angular based on the response from an API? Currently, when I retrieve data from the API, I am seeing the same information displayed in both dropdown controls. Is there a way to assign dynamic values to the "list" prop ...

`How can I sort information based on a chosen parameter?`

Is it possible to combine the two conditions into one within the function onSelectedReport()? Representing these conditions in HTML would result in: HTML: <div *ngFor="let report of reports"> <div *ngFor="let i of income"> <di ...

Error Encountered in Cypress: "Tried to wrap warn but it is already wrapped"

Objective: Utilize Cypress and Typescript to test for warnings and errors on the console. Error Encounter: An attempt was made to wrap warn, which is already wrapped. Snippet: describe.only("Unauthenticated User", () => { it("No C ...

Using TypeScript, you can replace multiple values within a string

Question : var str = "I have a <animal>, a <flower>, and a <car>."; In the above string, I want to replace the placeholders with Tiger, Rose, and BMW. <animal> , <flower> and <car> Please advise on the best approach ...

react-mock-store: Error - the middleware function is not defined

In my current setup, I am utilizing jest for testing with React and Typescript. import configureStore from "redux-mock-store"; import thunk from "redux-thunk"; const mockStore = configureStore([thunk]); it("should handle fetchCha ...

When employing CDK to configure an SNS topic Policy Statement with actions limited to ["sns:*"], the CloudFormation output may display a warning message stating "Policy statement action is not within the service scope."

Encountering an issue when attempting to reference all SNS actions with * in CDK. const MyTopicPolicy = new sns.TopicPolicy(this, 'MyTopicSNSPolicy', { topics: [MyTopic], }); MyTopicPolicy.document.a ...

Pinia store encountering a Typescript Vue component issue: error message "The property 'testStore' does not exist on the 'CreateComponentPublicInstance' type."

Within my <script setup> block, I have imported my testStore. However, whenever I attempt to use this.testStore.name in my Vue file, Vetur displays the following error: Property 'testStore' does not exist on type 'CreateComponentPublic ...