Ensuring a string is a valid key value within an interface using TypeScript

const word = 'world';
const bar = 'bar';
const testString = 'test';

interface validation {
  greet: 'world';
  food: 'bar';
};

Is there a way to verify if a string matches a key value in an interface?

In this case, I want word and bar to succeed, but testString to fail. How can this be achieved?

Appreciate any help!

Answer №1

Let's explore this scenario:


const word = 'hello';
const phrase = 'foo';
const saying = 'example';

interface instance {
    hi: 'hello';
    bar: 'foo';
};

type FindMatch<Obj, Str extends string> = {
    [Property in keyof Obj]: Str extends Obj[Property] ? Str : never
}[keyof Obj]

type Outcome1 = FindMatch<instance, 'hello'> // hello
type Outcome2 = FindMatch<instance, 'foo'> // foo
type Outcome3 = FindMatch<instance, 'example'> // never

FindMatch:

Property - denotes each key Obj - signifies an object, specifically the instance interface.

This custom type goes through each key of the Obj (instance) and determines if Obj[Property] matches with the second parameter Str (string or phrase). If it does match, then use Str as the value for that property, otherwise use never. The line [keyof Obj] at the end of the type fetches a union of all values in the object. If any value matches Str, we will get Str | never. Since never is a bottom type and can be assigned to any type, the union of Str | never simplifies to just Str.

If you only want to obtain a boolean output from FindMatch, where true indicates a match exists and false implies no match. You can add a conditional type that checks whether the result extends never or not:



const word = 'hello';
const phrase = 'foo';
const saying = 'example';

interface instance {
    hi: 'hello';
    bar: 'foo';
};


type IsNever<T> = [T] extends [never] ? true : false

type FindMatch<Obj, Str extends string> = IsNever<{
    [Property in keyof Obj]: Str extends Obj[Property] ? Str : never
}[keyof Obj]> extends false ? true : false

type Result1 = FindMatch<instance, 'hello'> // true
type Result2 = FindMatch<instance, 'foo'> // true
type Result3 = FindMatch<instance, 'example'> // false

Playground

If you wish to trigger an error, follow this example:


const word = 'hello';
const phrase = 'foo';
const saying = 'example';

type instance = {
    hi: 'hello';
    bar: 'foo';
};

type Values<T> = T[keyof T]

type Validate<Obj extends Record<string, string>, Key extends Values<Obj>> = Obj

type Output1 = Validate<instance, 'hello'> // true
type Output2 = Validate<instance, 'foo'> // true
type Output3 = Validate<instance, 'example'> // false

https://www.typescriptlang.org/play?#code/FDDGHsDsGcBcAI4CcCWkDm8C88DkB3cJAGwBNcBuMKORWVDAJmzwCMBDJS6mBZNdAGYWuWAFM43YLACeABzHwxAD3YBbOcUU4A3sHgH4ACzHFi4AFx5CJclUPwAZuEttO3AL5Vp8xQDV2YgBXCQAeABUAPhZwgG0AazEZcEd4cIBdEFkFeABBaGgxJFhQgHlWACslZXFIUmh4ACUxCCRSUP4MABo6GvANuyCDpd87moJKZAAnKQB5bAjIVO4cwAOykAbqd60AE4ZGIUCIkOJnmvbBiEjAYoOSSlfjamPN7LynYeMNWfWHDRhoOiHB50CfluDHcvjcODigROxsBppPMNpgTvNOAs05kie7fDaiderNCafLoAlTIagAoQkFAvyhyQxnaalBAxPWoyOGDAQajNAvjykixdayhWDqCootbbuh9OCYZAkoRXYYYOIn3vDNhqcbmbMCJflTYcTnp0ZWThht1wdDSvblXSXdRHACCsGuINAw4OnpqlxOWOPURROS3MZJPycmrxdPBABBgTkvvIl1SrGBiWhTDGY5E2lmryTdJzeRT9REziEkOrEMUAFXHIDX-MwJAIDZUXBOad202MOGTuGsBCokLIgvpHNVSxTJTW-I6Sz8RFonzrHSQUonTOdmJBMTNRCljDU/P0ovMPKiBLsu-A63hMQ41rl44SPHzJuCZiOVadi-example-link

You may have observed that I replaced interface instance with type instance. This change was intentional because I added a constraint to the Validate type. The first argument must extend Record. Unlike interfaces, Records are indexed types in TypeScript by default. For more information, refer to these discussions here and here.

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

Errors occur when passing an object to the redux store in a TypeScript project due to a mismatch

I am encountering an issue where I need to pass a datum object to a redux store without triggering TypeScript errors import { TreeNodeDatum } from 'react-d3-tree/lib/types/common'; import { HierarchyPointNode } from 'd3-hierarchy'; con ...

Tips for constructing node.js projects using local versions of the dependencies?

Recently, I've been tackling a rather intricate node.js project (find it at https://github.com/edrlab/thorium-reader/) while trying to incorporate local versions of certain dependencies. Surprisingly, I can successfully build and execute the project ...

Rearranging items within an array in a React component

Currently, I am facing a situation where I have created a list that dynamically adds a React Node upon clicking a button. The final layout of the model looks like this: https://i.sstatic.net/fG37r.png Here is the code snippet for your reference: import ...

Locate and refine the pipeline for converting all elements of an array into JSON format using Angular 2

I am currently working on implementing a search functionality using a custom pipe in Angular. The goal is to be able to search through all strings or columns in a received JSON or array of objects and update the table accordingly. Here is the code snippet ...

Encountering a problem when attempting to iterate through Observable Objects in Angular 2

I've hit a roadblock trying to iterate through the observable object in my users service. The error thrown by Chrome's console is: error_handler.js:47 EXCEPTION: undefined is not a function Below is the code causing the issue: users.compone ...

Encountering a Difficulty while attempting to Distinguish in Angular

I am currently working on a form where I need to dynamically add controls using reactiveForms. One specific task involves populating a dropdown menu. To achieve this, I am utilizing formArray as the fields are dynamic. Data: { "ruleName": "", "ruleD ...

When using Array.find() in TypeScript, the Subscribe function does not get called

I am currently diving into Typescript and web development, but I've encountered a peculiar issue when subscribing to an event that's leaving me stumped. In my service, I'm using a BehaviorSubject to store a carId, and on a page where there&a ...

The `message` binding element is assumed to have a type of `any` by default

I am trying to send data from parent component to child component, but I am encountering an error: Binding element 'message' implicitly has an 'any' type. Can someone assist me with my code? const Forms = () => { const [messageTe ...

Encountering an Issue: The formGroup function requires an instance of a FormGroup. Kindly provide one

I am a beginner with Angular 2 and despite reviewing numerous stack overflow answers, I still can't resolve my issue. I have recently started learning about angular reactive forms and wanted to try out my first example but I'm facing some diffic ...

Deliver router services for central Angular 2 elements

I am working on an ng2 app where I have the app/app.module and core/core.module. In the core modules, there are some modules that are used at the app top level and only once as mentioned in the official documentation. However, one of these modules requires ...

Using Typescript to mute audio elements within HTML documents

I have a scenario where I want to mute audio that automatically plays when the screen loads. In order to achieve this, I am attempting to add a button that can toggle the audio mute functionality using Typescript within an Angular4 application. The code sn ...

What is the best approach to creating a Typescript library that offers maximal compatibility for a wide range

My Vision I am aiming to develop a versatile library that can cater to both JavaScript and TypeScript developers for frontend applications, excluding Node.js. This means allowing JavaScript developers to utilize the library as inline script using <scri ...

Types for Vue libraries

I am in the process of developing a Vue library as an NPM package with the intention of making it available for use in other projects. The main entry point is main.ts, which exposes a plugin and some commonly used functions. Here's a simplified examp ...

What is the best way to use Immer to update Zustand state when incorporating objects that are added through a controlled form using React-Hook-

Having some trouble with integrating Zustand and Immer using React-Hook-Form. My goal is to capture a series of values from a form, store them in a list, and allow for the addition of new objects to that list. In this scenario, the user inputs data for a ...

Directing non-www to www in Next.js has never been easier

Based on the information I've gathered, it seems that using www is more effective than not using it. I am interested in redirecting all non-www requests to www. I am considering adding this functionality either in the next.config.js file or, if that& ...

What is the role of authguard in securing routes?

When developing an application, I encountered the need to implement authorization to protect routes using AuthGuard. However, I now face the challenge of securing child routes based on a role system obtained from the backend during login. For example, if t ...

[Nuxt.js/Typescript] Accessing Vuex data in Nuxt.js using Typescript

Hello, I am new to Typescript and I have encountered an issue with setting Objective Data to Vuex store. Here is the Objective data of Users (also known as account). # models/User.ts export interface IUser { email: string | null name: string | null ...

Asyncronous calls in Angular involve executing tasks without

The issue seems to be related to the timing of updates for the controlSelected and isAssessmentDataLoading variables. The updateQuestions() method is invoked within the ngOnInit() method, which is triggered when the component is initialized. However, the ...

Converting an array of object values to an Interface type in Typescript

In my JSON document, I have an array named dealers that consists of various dealer objects like the examples below: "dealers" : [ { "name" : "BMW Dealer", "country" : "Belgium", "code" : "123" }, { "name" : ...

I have been attempting to incorporate icons from fluent ui northstar into the fluent ui dropdown component, but unfortunately, there is a lack of adequate documentation

I attempted to use renderItem to include a divider and Icon in a Fluent UI dropdown menu, but the icons are not visible. Even the default value does not display the icons, and the dropdown menu does not appear after clicking. import * as React from " ...