Creating a typescript array with values matching keys in an object: How to do it?

How can I define MyInterfaceKeys in the given code?

interface MyInterface extends Record<string, any> {
   Appearance?: "default" | "primary" | "link";
   Size?: "small" | "medium" | "large";
   Color?: string;
   Block?: boolean;
}

type MyInterfaceKeys = (keyof MyInterface)[]

// Successful => MyInterfaceKeys === ["Block", "Color"] 
// Successful => MyInterfaceKeys === ["Appearance"]

// Error => MyInterfaceKeys === ["UnknownKey"]

I actually intend to convert object properties into a union of literals:

type MyInterfaceKeys = ("Appearance" | "Size" | "Color" | "Block")[]

Answer №1

When it comes to TypeScript, object types have a unique set of keys that are either known or defined by an index signature. Known keys are specific string or number literals, while index signature keys can represent multiple possible keys at once. With the introduction of pattern template literals and symbols in index signatures, the flexibility has increased significantly.

type Foo = {
    [k: string]: 0 | 1
    x: 0,
    y: 1
}

In this example, "Foo" has two known keys - "x" and "y", along with one index signature key "string". Similarly, the type "MyInterface" includes four known keys and inherits a "string" index signature key from extending Record.


The use of the keyof operator helps create a union of all keys within a type. For instance, for "Foo", the result is "x" | "y" | string. On the other hand, for "MyInterface", it becomes "Appearance" | "Size" | "Color" | "Block" | string.

Since string literals are subtypes of strings, when combined, they reduce to just "string." This means that using keyof on types like "Foo" and "MyInterface" will only yield "string," as it encompasses all the string literal keys.

Therefore, extracting only known keys without interference from index signature keys can be achieved through strategic refactoring:

interface MyKnownInterface {
    Appearance?: "default" | "primary" | "link";
    Size?: "small" | "medium" | "large";
    Color?: string;
    Block?: boolean;
}

interface MyInterface extends Record<string, any>, MyKnownInterface { }

type KK = (keyof MyKnownInterface) & string

type MyInterfaceKeys = (keyof MyKnownInterface)[]

By maintaining a separate interface for known keys and leveraging type manipulations, a cleaner separation between known and index signature keys can be achieved.


Alternatively, utilizing techniques like key remapping in mapped types can suppress unwanted index signatures and focus solely on known keys:

type KnownKeys<T> = keyof {
    [K in keyof T as {} extends { [P in K]: any } ? never : K]: never
}

type MyInterfaceKeys = (KnownKeys<MyInterface>)[]

These methods ensure a clear distinction between known keys and index signature keys in TypeScript interfaces, enhancing code readability and maintainability. The code snippet provided illustrates how these concepts can be implemented effectively.

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

Type definitions in Typescript for the style property of Animated.View

One of my components has a Props interface that extends ViewProps from React Native, like this: export interface Props extends ViewProps { // Custom props } As a result, this also extends the style prop. However, I am facing an issue while using Animat ...

Identify when the Vue page changes and execute the appropriate function

Issue with Map Focus Within my application, there are two main tabs - Home and Map. The map functionality is implemented using OpenLayers. When navigating from the Home tab to the Map tab, a specific feature on the map should be focused on. However, if th ...

Having trouble with vscode compiling the typescript file?

Even though I diligently followed the tutorial provided by vscode on compiling typescript code, I encountered a problem. The configurations were set up as per the instructions in the tutorial, but when I tried to run the code without debugging, I received ...

Loopback 4: Transmitting associated information

In my registration form, I have the option to add one or more addresses for a user. In order to streamline this process, I am using loopback: { "id": 0, "name": "User Name", "addresses": [ { "street": "xx", "city": "xx", "count ...

The underscore convention for defining members in Typescript allows for clear and concise

Let's talk about a class called Email class Email { private _from: string; private _to: Array<string>; private _subject: string; } When an email object is created, it will look something like this: { _from:'', _to:'&apo ...

Angular2 is throwing a Typescript Reference error because 'is not defined' in the context

I've been grappling with this specific error for the last couple of hours, and it's definitely not a run-of-the-mill undefined error. The issue arises when I define a value in the webpack plugins section (for a global variable) to serve as an API ...

Generating Angular component based on selector

I developed an app that showcases various visualizations of RxJS operators such as rx-map, rx-filter, rx-reduce, and more. The visualizations function correctly on their own. Now, I am looking to enable users to select which visualization they want to view ...

Explore the functionalities of RxJS filter operators by applying them to a stream of arrays

Is it possible to work with a stream of arrays, such as filtering, and then emit the arrays again without concatenating the elements of the arrays or using regular array operators? For example, if I have an observable containing a single array, I can perfo ...

When using EcmaScript imports with the 'node16' or 'nodenext' module resolution, it is important to include explicit file extensions in relative import paths. For example, did you intend to use './*.js'?

Within my package.json file, I have set "type": "module" and utilize SWC for compiling TypeScript code. For imports, I utilize import Example from './example'. In addition, I use the following script: "start": " ...

The error "Failed to log in. Cannot read property getPackageManager of undefined in Angular 2

Recently, I came across the nativescript-appList Plugin, but unfortunately encountered a runtime error stating "Cannot read property getPackageManager of undefined." My code implementation within the constructor of an Angular2-NativeScript project is as f ...

What's the best way to address this blind spot?

Exploring the world of TypeScript has left me puzzled by a scenario where TypeScript does not perform type checking as expected. I'm running into an issue where 'this.a.method()' appears to be error-free when it should actually throw an erro ...

Type inference in Typescript is especially powerful when used in conjunction with decorators

I am curious as to why the compiler in Typescript cannot infer the new type of a class when decorators or annotations are used. Interestingly, if I use the traditional ES5 method (manually calling the decorator), it works without any issues. Here is an ex ...

Encountering issues while trying to run npm install for an Angular 7 application, specifically receiving an error stating: "Module not found: @angular-devkit/build-ng-packagr." This error is hindering

I don't have much experience with JavaScript, node, npm, Angular, etc. My expertise lies in TypeScript as I am still a beginner. However, I recently inherited an application that requires maintenance to resolve a cross-site cookie issue. As I attempt ...

Ensure that only numerical values in decimal form are permitted when entering data in Angular

How can I restrict user input to only decimal values? Here is the HTML code for my input field: <label for="conversion-factor">Conversion Factor:</label> <input type="text" class="form-control form-control-sm" id="conversion-factor" ...

Designing a filter system based on price ranges using checkboxes

import { useState } from 'react'; const Shop = ({capes}: Props)=>{ const[checkVal1, setCheckVal1]= useState(0); const[checkVal2, setCheckVal2]= useState(0); const[checkVal3, setCheckVal3]= useState(0); const[checkVal4, setCheckVa ...

Highcharts3d was already defined locally, preventing the import declaration from being recognized with error code TS2440

Struggling to get my application to run in VS code due to this persistent error: error TS2440: Import declaration conflicts with local declaration of 'Highcharts3d' Any assistance in resolving this issue would be greatly appreciated. import ...

Encountering SUID Sandbox Helper Issue When Running "npm start" on WSL with Electron and Typescript

Can anyone help me with this issue? I have Node v8.10.0 and I'm attempting to follow a beginner tutorial on Electron + Typescript which can be found at the following link: https://github.com/electron/electron-quick-start-typescript Here is the full e ...

What is preventing me from importing moment into my TypeScript React Native project?

Looking to incorporate MomentJS into my ReactNative component with TypeScript. Successfully imported the library's d.ts file from the node_modules directory. This is how I am importing and utilizing the library: import * as moment from "moment"; con ...

When attempting to change a Component's name from a string to its Component type in Angular 9, an error is thrown stating that the passed-in type is

When working with Template HTML: <ng-container *ngComponentOutlet="getComponent(item.component); injector: dynamicComponentInjector"> </ng-container> In the .ts file (THIS WORKS) getComponent(component){ return component; //compo ...

Working with Angular 4: Utilizing HttpResponse in a Component

I am attempting to retrieve the response from my POST request using Angular 4. Below is the code I am using: app.component.html: `findAccordiSmall(pagination: Pagination) { this.accordiListTableLoading = true; debugger; this.ac ...