Tips for working with Typescript: utilizing the default value for a non-existent computed property

When utilizing Computed Property in javascript, I can structure my code as follows

const default_values = {a:"debug",b:"info",c:"warning"};

function execute(y) {
    let x = default_values[y] || default_values.a /* if y is not a or b or c */
}

However, how can I achieve the same functionality in TypeScript ?

const default_values = {a:"debug",b:"info",c:"warning"};

function execute(y: string) {
    let x = default_values[y] || default_values.a;
}

I encountered an error during compilation

error TS7053: Element implicitly has an 'any' type because expression of type 'string' can't be used to index type '{ a: string; b: string; c: string; }'.

No index signature with a parameter of type 'string' was found on type '{ a: string; b: string; c: string; }'.

I have tried searching SO but could not find a relevant answer, except for How to set a variable if undefined in typescript?, which is not applicable to my issue

Therefore, I updated my ts code to the following, although it seems more cumbersome compared to the original js code

function execute(y: string) {
    let x:string
    if (y!='a' && y!='b' && y!='c') {
        x = default_values.a;
    } else {
        x = default_values[y]
    }
}

---- update ----

@captain-yossarian provided one solution, e.g.

const default_values: Record<string, string> = { a: "debug", b: "info", c: "warning" };

Another method to resolve the issue is by using keyof typeof, refer here https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-1.html#keyof-and-lookup-types for additional details, e.g. "In JavaScript it is fairly common to have APIs that expect property names as parameters, but so far it hasn’t been possible to express the type relationships that occur in those APIs.", precisely reflecting my scenario here!!

const default_values = {a:"debug",b:"info",c:"warning"};

function execute(y:keyof typeof default_values) {
    let x = default_values[y] || default_values.a;

}

This solution was derived from How to dynamically access object property in TypeScript

Answer №1

def_val is determined by TypeScript as

const def_val: {
    a: string;
    b: string;
    c: string;
}

The y argument is of type string

def_val expects keys 'a' | 'b' | 'c'. This means that TypeScript only allows the use of these specific keys with def_val. Since the string type is broader than 'a' | 'b' | 'c', an error occurs.

y:string indicates that you can pass the property foo, however, def_val['foo'] is not safe as foo does not exist in def_val.

To resolve this, you should provide an explicit type for def_val:

const def_val: Record<string, string> = { a: "debug", b: "info", c: "warning" };

function work(y: string) {
    let x = def_val[y] || def_val.a // okay
}

If you are unable to use an explicit type on def_val, you can provide def_val as an argument to work:


const def_val = { a: "debug", b: "info", c: "warning" };

function work<Def extends Record<string, string>>(def: Def, y: string) {
    let x = def[y] || def.a
}
work(def_val, 'foo') // okay

You can also implement a custom type guard:


const def_val = { a: "debug", b: "info", c: "warning" };

const isValidKey = (key: string): key is keyof typeof def_val =>
    /a|b|c/.test(key)

const work = (y: string) => isValidKey(y) ? def_val[y] : def_val.a

work('foo') // okay

P.S. Refer to this documentation for information on using built-in utility types like Record, Partial, etc...

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

Firebase Angular encountering issues with AngularFirestoreModule

I have encountered a challenge with Firebase authentication in my Angular applications. Due to updated read and write rules that require auth!=null, I successfully implemented Firebase authentication in one of my apps using Angular 13. Now, I am trying to ...

Invoking a controller from another controller in the Express framework using Typescript

Currently, I am trying to call a controller from another controller in my code. While attempting to pass parameters using {params: {task_id: String(task_id), result}}, TypeScript is flagging an error indicating that res does not have all the required attri ...

Unable to grasp the mistake

My code contains a function: buy() { return new Promise((resolve, reject) => { this.http.request('http://192.168.1.131:8888/generatetoken.php') .subscribe(res => { resolve(res.text()); }); }).then((key) => ...

What is the correct way to format React's dispatch function in order to utilize a "then" method similar to a Promise?

I'm working on a simple app that dispatches an action upon first load to populate the store. However, I'm facing an issue with trying to run a then method on dispatch, as typescript is throwing errors. (As per redux's documentation, the ret ...

Is it feasible to amalgamate the states of several child components into a single parent component state in NextJS/React?

Issue I am faced with the challenge of handling multiple Child components that can pass their state to a Parent component. Now, I am looking to render several Parent components within a Grandparent component and merge the states of each Parent into a sing ...

A method for increasing a counter using only an instance of a class or function without accessing its methods or properties in Javascript

Looking at the task ahead, let increment = new Increment(); I have been tasked with creating a Javascript class or function called Increment in order to achieve the following: console.log(`${increment}`) // should output 1 console.log(`${increment}`); ...

Tips for creating a page component in Next.js using props?

I've encountered an issue while trying to properly annotate the parameters of the Home function component. My initial attempt was to use: { events }: { events: Event[] }, but TypeScript is throwing an error, stating that Property 'events' do ...

The issue is that TypeScript is indicating that the type 'string | string[]' cannot be assigned to the type 'string'

https://i.sstatic.net/ZaJvb.pngI recently upgraded to Angular 16 and encountered an issue with an @Input() property of type string | string[]. Prior to the upgrade, everything was functioning correctly, but now I am experiencing errors. I am uncertain abou ...

Combining the output of two Observables through the CombineLatest method to generate a

I possess two separate collections of information: Data Model: taxControlReference [ { "providerId": "HE", "taxTables": { "STAT": [ 1 ] } }, ...

The Element type does no feature a Typescript property

Despite my attempts to include a declaration file and various other solutions, I'm still struggling with this issue: The goal is to define the visible property as a function on the HTML Element object. However, the linter keeps flagging visible with ...

What is the best way to display the output of a Set-typed function using a class key in my code?

I'm currently working on developing a function that can return a Set containing the keys of a class. However, I am struggling to determine the correct definition for this function. class Bot { public no01: number; public no02: number; construct ...

Encountering an issue with accessing a property in Angular's TypeScript module

I encountered an issue while trying to access a property of a static array that I created in a TypeScript class. The error message I received is as follows: ERROR TypeError: Cannot read property 'forbiddenProjectNames' of undefined Below is th ...

Is it necessary for me to individually download every x.d.ts file for my application?

Do I need to include the following line for type checking when using fs.readFile in TypeScript? /// <reference path="node.d.ts" /> Is it considered 'best practice' to download and maintain the most recent version of node.d.ts file in my ...

Is there a way to retrieve the timezone based on a province or state in TypeScript on the frontend?

I've been working on an angular app that allows users to select a country and state/province. My current challenge is determining the timezone based on the selected state/province, with a focus on Canada and the US for now (expanding to other countrie ...

Injecting services with an abstract class is a common practice in Angular library modules

In my development workflow, I have established an Angular Component Library that I deploy using NPM (via Nexus) to various similar projects. This library includes a PageComponent, which contains a FooterComponent and a NavbarComponent. Within the NavbarCom ...

What is the best way to form a new type that encompasses all shared properties found within a union of types?

Is there a method to safely map over the union of arrays without hard-coding specific types? When attempting to calculate newArr1, an error is encountered: Property 'field2' does not exist on type 'Common<A, B>'. How can this err ...

Using Typescript for testing React components: successfully passing an array of objects as props

My current approach involves passing an array of objects to mock component data for testing: const mockPackage = { id: '1232-1234-12321-12321', name: 'Mock Package', price: 8.32, description: 'Mock description', glo ...

The Vue Prop does not have an initializer and is not explicitly assigned in the constructor

Currently, I am utilizing props in Vue Class components. Within the constructor, I have defined the props without a value. This setup compiles and operates smoothly, except after the most recent VS Code / TSLint update, a warning message emerges: The pr ...

What is the process for updating information once the user has verified their email address on Supabase using Next.js

After a user signs up using a magic link, I want to update the profiles table in my database. Below is the code snippet I am currently using: Login.tsx import { useState } from "react"; import { supabase } from "../lib/initSupabase"; c ...

Where does tsc retrieve its definitions from when utilizing npm definitions?

After transitioning from using typings to just relying on npm, I noticed that the @types directory in node_modules is present, but there are no additional files required. Previously with typings, I always had to include the index.d.ts file within the typi ...