Tips for pulling out specific keys from a typed object using an index signature

TL;DR

Query: How do I create a type converter in TypeScript that extracts the defined keys from objects typed with index signatures?


I am looking to develop a type "converter" in TypeScript that takes a type A as input and outputs a new type B with keys representing all the defined keys in A, accepting only strings as values, as shown in the example below:

type AToB<
   T,
   K extends keyof T = keyof T
> = { [id in K]: string };

const a = {
    x : {}
}

const b : AToB<typeof a> = {
    x : "here it works"
}

https://i.sstatic.net/DKwu9.png b works

However, when I apply this to an object that already has a type with an index signature defined, the keyof operator does not recognize the defined keys, as shown in the following example:

type AWithSig = {
    [id : string]: {};
}

const aSig : AWithSig = {
    y: {},
    z: {}
}

const bSig : AToB<typeof aSig> = {
    y : "here the keys",
    z : "are not shown :("
}

I tested this in the TSPlayground (link here) and it does not recognize the defined keys in aSig.

https://i.sstatic.net/kpc14.png bSig doesn't work

https://i.sstatic.net/UtDs8.png bSig works if I define the keys manually

Answer №1

It's not AtoB that's the problem, it's actually the type of aSig.

When you specify the type of a variable without a union type, that becomes the fixed type for the variable. It does not gain any additional specificity from the expression used to initialize it.

By the time you declare const aSig: AWithSig = ⋯, you've already lost the specific type you wanted.

Assuming you annotated the variable to simply validate that the initializing expression was a valid AWithSig, you can use the satisfies operator to perform the check on the expression and then let the compiler infer the type of aSig automatically:

const aSig = {
    y: {},
    z: undefined // error!
//  ~ <-- undefined is not assignable to {}
} satisfies AWithSig;

Oops, I made a mistake and the compiler caught it:

const aSig = {
    y: {},
    z: {}
} satisfies AWithSig // okay

Now, the type of aSig is

/* const aSig: {
    y: {};
    z: {};
} */

which will now work correctly with AtoB:

const bSig: AToB<typeof aSig> = {
    y: "this won't work :(",
    z: "abc"
}
// const bSig: AToB<{ y: {}; z: {}; }, "y" | "z"> 

Link to Playground with Code

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

Why aren't special methods/mixins for Sequelize associations being generated?

According to the documentation available at https://sequelize.org/docs/v6/core-concepts/assocs/#special-methodsmixins-added-to-instances: When establishing an association between two models, special methods are introduced that enable instances of those mo ...

Ways to retrieve data object within an HTMLElement without relying on jQuery

Within my web application, I have successfully linked a jQuery keyboard to a textbox. Now, I am seeking a way to explicitly call the keyboard.close() function on the keyboard as I am removing all eventListeners from the text field early on. To achieve thi ...

ParcelJs is having trouble resolving the service_worker path when building the web extension manifest v3

Currently, I am in the process of developing a cross-browser extension. One obstacle I have encountered is that Firefox does not yet support service workers, which are essential for Chrome. As a result, I conducted some tests in Chrome only to discover tha ...

Set values to the inner property of the object

In my configuration file, I have set up nested properties as shown below export class Config { public msalConfig: { auth: { authority: string; clientId: string; validateAuthority: boolean; redirectUri: ...

Unable to locate module '...' or its associated type declarations. Issue encountered in NextJS with TypeScript integration

Within my NextJs project, I have generated a cookie.ts file in the utils directory. Upon running "npm run dev" and accessing my application on localhost:3000, everything runs smoothly with no errors, and the code in my utils/cookie.ts file is retrieved suc ...

Module not located following the completion of project compilation

Below is the content of my package.json file: { "main": "./build/app.js", "types": "./build/app.d.ts", "scripts": { "start": "tsc && node build/app.js", "dev": "concurrently \"tsc -w \" \"nodemon ...

Verify the specific type conditions of a key value in a mapped type

I am attempting to achieve the following: If the actions property exists, and there are callbacks within the actions properties that return a string, then return X or Y. The above code is expressed as: // type MappedType<T> = { // [K in keyof T]: ...

Is it possible to update input form fields in an Angular application?

I am currently designing a straightforward web page featuring a modal for creating a new object named Partner and sending it to the server. The page also includes multiple input fields to showcase the newly created data. In this project, I am utilizing Ang ...

What is the best way to integrate model classes within an Angular module?

I have a few classes that I want to keep as plain bean/DTO classes. They are not meant to be display @component classes, @Pipe classes, or @Directive classes (at least, that's what I believe!). I am trying to bundle them into a module so that they ca ...

Is it possible to retrieve the child state value in the parent component using useRef in ReactJS with TypeScript (hooks)?

I am currently learning Typescript and I am trying to figure out how to pass child state values to the parent component using a ref when a button is clicked in order to update the reducer values. However, I keep running into errors when I try to pass a ref ...

Is Angular 9's default support for optional chaining in Internet Explorer possible without the need for polyfill (core-js) modifications with Typescript 3.8.3?

We are in the process of upgrading our system to angular 9.1.1, which now includes Typescript 3.8.3. The @angular-devkit/[email protected] utilizing [email protected]. We are interested in implementing the optional chaining feature in Typescript ...

Configuring TypeScript for Firefox to recognize specific types such as browser.storage

As per the documentation from Mozilla, I should be able to utilize browser.storage.sync.get in my extension. However, I am encountering some challenges in getting TypeScript to recognize that I can use browser. I have attempted the following (which has wo ...

Combining Promises in Typescript to create a single Promise

Is there a way for me to return the temp_data object filled with data after using .map? Currently, it always returns undefined because I initialize temp_data as an empty object. But if I don't do this, I can't use LooseObject. Can anyone suggest ...

When running `npm test`, Mocha TS tests encounter failure, but the issue does not arise when executing them

When running tests in my Typescript nodejs project, I use the following command: mocha --compilers ts:ts-node/register,tsx:ts-node/register The tests run successfully with this command. However, when I try to run them using npm test, I encounter the foll ...

Component coding in Angular 2 allows for seamless integration and customization of Material

I am looking to initiate the start.toggle() function (associated with Angular 2 material md-sidenav-layout component) when the test() method is triggered. How can I execute md-sidenav-layout's start.toggle() in the app.component.ts file? app.componen ...

How do @material-ui/core and @types/material-ui interact with each other?

Exploring a sample project that utilizes material-ui. Upon inspecting the package.json file, I noticed the inclusion of the following packages: { ... "dependencies": { "@material-ui/core": "^1.4.1", ... }, "devDependencies": { "@types ...

What is the best way to obtain a reference to an instance of my Angular 2 directive?

Angular 2 rc 5 was written using typescript 1.9 I am trying to access the instance of my attribute directive. Although I am using ViewChild, which typically works with components, it is giving me a handle to the element containing the directive. template ...

How can you inject the parent component into a directive in Angular 2, but only if it actually exists?

I have developed a select-all feature for my custom table component. I want to offer users of my directive two different ways to instantiate it: 1: <my-custom-table> <input type="checkbox" my-select-all-directive/> </my-custom-table> ...

The imported package is not functioning properly within the project

I've recently developed a Typescript Package and I want to test it in an application before publishing it on NPM. The main file (index.ts) of the package is structured like this => import Builder from './core/builder'; export default ...

How can we properly retrieve data in order to update the user interface using the useEffect hook and useState in React

I'm diving into the world of Next.js 13 for the first time, attempting to retrieve a cart object from the API and display it on the UI. Utilizing useState to hold the cart object and useEffect to fetch it. However, upon calling setCart(), the UI fail ...