Can I define a string to correspond to a property name in an interface using TypeScript?

I have the following interface as an example:

interface Account {
    email: string;
    enabled: boolean;
}

I want to create a method that will return default values for the fields in this interface. This is what I have so far:

function defaultValue(propName:string) {
    if (propName === 'email') return '';
    if (propName === 'enabled') return true;
}

While this method works fine, it's not foolproof. If any property within Account changes, the defaultValue() method may still compile correctly even though it's now incorrect.

Is there a way to specify that propName should match the type of a property name in Account? Or is there another effective pattern I can implement to ensure type checking in this scenario?

Answer №1

To ensure that a variable is never at the end of if statements, you can create a function and use keyof T to obtain a union of keys:

function assertNever(v: never): never {
  throw new Error("Should never happen!");
}

function defaultValue(propName:keyof Account) {
    if (propName === 'email') return '';
    if (propName === 'enabled') return true;
    assertNever(propName);
}

Playground Link

You can enhance the function by making it generic for better caller experience, but this may require type assertions in the implementation.


function defaultValue<K extends keyof Account>(propName:K): Account[K] {
    if (propName === 'email') return '' as Account[K];
    if (propName === 'enabled') return true as Account[K];
    assertNever(propName);
}

let email = defaultValue('email') // string
let enabled = defaultValue('enabled') // boolean

Playground Link

Answer №2

To achieve this, consider specifying a more precise string type for the propName parameter. Utilize a switch statement to enable smart type inference, ensuring that you return a value matching the return type for each potential branch within the function.

interface Account {
    email: string;
    enabled: boolean;
    shouldError: boolean;
}

function defaultValue(propName: keyof Account): Account[keyof Account] { // indicates error if not all paths return a value
    switch (propName) {
        case "email":
            return "";
        case "enabled":
            return true;
        // Uncomment the following code block to rectify the error
        // case "shouldError":
        //     return true;
    }
}

TypeScript Playground Link

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

Guide to implementing a specified directive value across various tags in Angular Material

As I delve into learning Angular and Material, I have come across a challenge in my project. I noticed that I need to create forms with a consistent appearance. Take for example the registration form's template snippet below: <mat-card> <h2 ...

What is the proper way to designate a data type for a variable?

I have a variable that is an array of string arrays. How do I declare it with a specific type in TypeScript? let map: Array<Array<string>> = [ ['key1', 'value1'],['key2', 'value2'] ] ; For instance, if ...

Why is a method for animation in Three.js + TypeScript only executed once?

This piece of code is currently functioning correctly, except for one issue - the lack of animation. The code draws an axis and a cube, but the cube should be animated. I only see the "animate" string in the console once. Solution: Replace this line reque ...

The change handler of the TS RadioGroup component, instead of returning an array of possible strings, returns a unique

My interface declaration for StopData is shown below: export interface StopData { stopName: string, stopType: 'stop' | 'waypoint' } I have implemented a radio group to choose the 'stopType', which consists of two radi ...

Invoking a functionality within a stream of events through an observable's subscribe

Service2.ts public flags$: BehaviorSubject<FlagName> = new BehaviorSubject<FlagName>("custom-flag-1"); This flag is set up as follows: private _setFlags = () => { const flagsData = this._customClient.getFlags(); if (f ...

When using string as a primitive type in Vue 3, what distinguishes the usage of PropType in props from not using it?

The documentation explains how Vue does runtime validation on props with defined types. To enable TypeScript to recognize these types, constructors are cast with PropType. The code example in the documentation uses success: { type: String }, whereas it c ...

Discovering errors within a reactive form in Angular using a loop to iterate through the FormArray

I am currently utilizing Angular CLI version 16.2.1. As I progress through a course, I am working with reactive forms. In a list of 'Recipes', I aim to include a Recipe with various inputs and a collection of ingredients. However, I am encounter ...

The type 'number' cannot be assigned to the type 'Element'

Currently, I am developing a custom hook called useArray in React with TypeScript. This hook handles array methods such as push, update, remove, etc. It works perfectly fine in JavaScript, but encounters errors in TypeScript. Below is the snippet of code f ...

Discover how to modify the URL in Angular

There are two distinct projects under consideration. Authentication Project: is hosted at http://localhost:4200. This project contains a login page where users can input their credentials to sign in. Upon successful login, the user will be redirected to ...

Is it possible for the ComponentRef.destroy() function to also handle unsubscription of the Components Event Emitters?

When we have a dynamic ComponentRef instance that was created dynamically (not declaratively), and we use the destroy() method on the instance, does it automatically unsubscribe any subscriptions to EventEmitter instances? For instance, if we have an outp ...

Using React.lazy, Suspense, and react-router-dom for code splitting in React does not function as intended

I'm currently working on setting up a simple example that is similar to the concept of lazy-loading route components as explained here: https://reactjs.org/docs/code-splitting.html#route-based-code-splitting The idea is to dynamically load ModuleOne ...

Issue during Docker build: npm WARN EBADENGINE Detected unsupported engine version

Currently, I am constructing an image based on mcr.microsoft.com/devcontainers/python:0-3.11-bullseye. In my docker file, I have included the following commands towards the end: RUN apt-get update && apt-get install -y nodejs npm RUN npm install np ...

Enhance your TypeScript module with parcel augmentation

I'm having issues shipping a library that utilizes typescript module augmentation with Parceljs. It appears that the module augmentations are not getting bundled properly, preventing me from utilizing the properties in the project that imports the lib ...

"Embedding a JSON object within a script tag in NextJS: A step-by-step

I've been working on configuring values within a Cookiebot initialization script tag in a NextJS project. The documentation suggests that I can set vendor properties by passing in a JSON object inside the script tag, like this (taken from this link): ...

With each click on "add to cart," a duplicate of the component will appear

Can you help me achieve a functionality where every time I click on "addCart", a new instance of the same component (another Bets>Example Number</Bets> const newBet: React.FC = () => { const [getAddCart, setGetAddCart] = useState([ <Be ...

Guide to Angular Interface Styling - Ambiguous Suggestions

After reviewing the Angular style guide for interfaces, I find two recommendations particularly perplexing: The suggestion to use a class instead of an interface for services and declarables (components, directives, and pipes) leaves me puzzled. Similarl ...

Invoke a component's function in a different component

Upon clicking the button, I am trying to execute the 'exportHistoryToCSV' method within this component, which in turn calls a method from another component. However, I am encountering an error. @UntilDestroy() @Component({ selector: 'd ...

Using Angular 2, you can pass an object as a parameter to a function

Is there a way to pass an object as a parameter in the DOM on this forum? Within my HTML code, I have the following: <div class="list-items"> <ul> <li *ngFor="let i of item"> <span (click)="onAdd({{newUser.us ...

Tips for bringing in a feature that is available in JavaScript but missing in TypeScript definitions and troubleshooting any resulting errors

Utilizing react-native-keyboard-aware-scroll-view, the library has the following exports in their code: export { listenToKeyboardEvents, KeyboardAwareFlatList, KeyboardAwareSectionList, KeyboardAwareScrollView } However, the index.d.ts file does n ...

The issue with ag-grid not displaying data when the column configurations are changed dynamically

I have been working with ag grid to display data in my component. I am fetching data through an API call and dynamically extracting the column names from the response to pass this information to the child component for display. However, the data is not sho ...