What is the method to create a universal type by utilizing attributes from a different type?

In building a type, I aim to create one that can accept the type of another object and generate a different type based on specific properties in that object. Illustrating this concept through code:

// Definition representing the types of inputs shown on UI and their corresponding return values
// e.g. colorHex UI control should return a string
type Inputs = {
  colorHex: string
  yearPicker: number
}

// Union of possible input types - colorHex or yearPicker
type InputTypes = keyof Inputs

// Defines an input with a type for UI representation and a label
type Input = {
  type: InputTypes
  label: string
}

// Represents a collection of inputs containing an ID and an object of Inputs
type Composition = {
  id: string
  inputs: Record<string, Input>
}

// Map of compositions
type Compositions = Record<string, Composition>

const comps: Compositions = {
  short: {
    id: 'short',
    inputs: {
      bgColor: {
        type: 'colorHex',
        label: 'BG Color',
      },
      year: {
        type: 'yearPicker',
        label: 'Count',
      },
    },
  },
}

// Desired output is a map of input key and the resulting 
// input value type received from a composition
type InputProps<T extends Composition> = {
  [P in keyof T['inputs']]: Inputs[T['inputs'][P]['type']]
}

// Expected input prop types for comps.short
type ShortProps = InputProps<typeof comps.short>;

// Expected result for ShortProps
type Expected = {
  bgColor: string
  year: number
};

// Function utilizing the input props as parameter
const someFn = (props: ShortProps) => {
    // props === { bgColor: string; year: number }
}

// The following is correct
someFn({ bgColor: '#000', year: 2020 })

// This is incorrect and should produce a type error
someFn({ bgColor: 0, year: '2020' })

Explore the playground link. Note that the final line may not give a type error even though it should.

Answer №1

Ensuring that the variable comps aligns with the type Compositions is crucial. Previously, there was a tendency to override the object's type information by explicitly annotating it as Compositions. In TypeScript 4.8 and beyond, a helper function must be utilized for this purpose:

function comp<C extends Compositions>(c: C) { return c }

const comps = comp({
  short: {
    id: 'short',
    inputs: {
      bgColor: {
        type: 'colorHex',
        label: 'BG Color',
      },
      year: {
        type: 'yearPicker',
        label: 'Count',
      },
    },
  },
});

However, starting from version 4.9, you can use the satisfies keyword like so:

const comps = {
  short: {
    id: 'short',
    inputs: {
      bgColor: {
        type: 'colorHex',
        label: 'BG Color',
      },
      year: {
        type: 'yearPicker',
        label: 'Count',
      },
    },
  },
} satisfies Compositions;

An error will now be generated on the final line, which is the desired outcome.

Playground (4.8-)

Playground (4.9+)

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

Implementing TypeScript type declarations for merging core types

Is there a way to perform type declaration merging in TypeScript for built-in types when using imports? I am currently attempting to merge interfaces as per the guidelines outlined in this documentation: https://www.typescriptlang.org/docs/handbook/declar ...

Encountered an issue in Typescript with error TS2554: Was expecting 0 arguments but received 1 when implementing useReducer and useContext in React

I've encountered several errors with my useReducers and useContext in my project. One specific error (TS2554) that I keep running into is related to the AuthReducer functionality. I'm facing the same issue with each Action dispatch. I've tri ...

Unable to employ a custom Typescript .d.ts file

Currently, I am delving into learning TypeScript and encountering a hurdle while attempting to define a class in a TypeScript definition file and then utilize it in a TypeScript file. The dilemma lies with a JavaScript "class" called "Facade," which serve ...

Formik's handleChange function is causing an error stating "TypeError: null is not an object (evaluating '_a.type')" specifically when used in conjunction with the onChange event in DateInput

When using the handleChange function from Formik with the DateInput component in "semantic-ui-calendar-react", I encountered an error upon selecting a date. https://i.stack.imgur.com/l56hP.jpg shows the console output related to the error. AddWishlistFor ...

What prevents me from extending an Express Request Type?

My current code looks like this: import { Request, Response, NextFunction } from 'express'; interface IUserRequest extends Request { user: User; } async use(req: IUserRequest, res: Response, next: NextFunction) { const apiKey: string = ...

What is the reason that setState functions properly when parsing each key separately, but fails when passed as an object?

Currently, I am delving into the world of React and TypeScript, but I have encountered a problem when trying to pass an object with a specific type in order to update the state. For some reason, the state remains unchanged. My approach involves using the ...

Creating dynamic Kafka topic names within a NestJS microservice

Currently in Nestjs, I have integrated Kafka as my message broker and specified the topic name like so: @MessagePattern('topic-name') async getNewRequest(@Payload() message: any): Promise<void> { // my implementation logic } I am curious ...

Error: Unable to locate the custom module - TS2307

We have recently taken over a Next + TypeScript website from another developer and are attempting to create a new component that is heavily based on an existing one. I have duplicated the entire component and its subfolders at the same level as the origina ...

The specified object '[object Object]' is not compatible with NgFor, which only supports binding to iterable data types like Arrays

I have some code that is attempting to access objects within objects in a template. <table class="table table-striped"> <tr *ngFor="let response of response"> <td><b>ID</b><br>{{ response.us ...

What is the method for extracting the types of parameters from a function type while excluding a single parameter?

Suppose I have the following interface that defines a function: export declare interface NavigationGuard { (to: RouteLocationNormalized, from: RouteLocationNormalized, next: NavigationGuardNext): NavigationGuardReturn | Promise<NavigationGuardReturn ...

The type 'string' cannot be assigned to the type 'T[keyof T]' within this context

I have a function that processes an array of Episodes and assigns data from an external file to the corresponding Episode based on a specified keyName: const assignDataFromExternalFile = (arrayToProcess: Episode[], filePath: string, keyName: keyof Episode) ...

What is the most efficient method to retrieve an API in Angular?

Recently, I dedicated some time to a personal Angular project. While working on it, I decided to experiment with making an API call to PokeAPI in order to retrieve the .svg image of a Pokemon along with its name. After successfully implementing this featur ...

What could be the reason for the absence of Mock Service Worker in a React project that has Typescript enabled?

After attempting to integrate Mock Service Worker into my React project with Typescript support, I encountered errors when running the npm install msw --save-dev command. The terminal displayed the following messages: PS F:\Programming\React Prac ...

How can we ensure a generic async function with a return type that is also generic in Typescript?

I'm currently working on a function that retries an async function multiple times before rejecting. I want to make sure the typescript typings of the retry function are maintained and also ensure that the passed function is of type PromiseLike. Creat ...

Error: Cannot access Angular 5 Title service at this time

When attempting to change the page title using BrowserModule, I encountered an issue. I added the BrowserModule and Title in the application module as shown here: https://angular.io/guide/set-document-title However, in a child module where I tried to add ...

Unlocking the secrets of integrating Vuex store with JavaScript/TypeScript modules: A comprehensive guide

I am working on a vue application and I have a query. How can I access the store from javascript/typescript modules files using import/export? For example, if I create an auth-module that exports state, actions, mutations: export const auth = { namesp ...

What is the method for accessing a validator that has been declared in a reactive form group while within the scope of a custom

Currently, I have a custom component that I am happy with and it is being used in a reactive form as shown below. private init() { const control4 = new FormControl("yy", Validators.pattern(".{2}")); const control5 = new FormControl("zz", Validators.pa ...

Using the -t or --testNamePattern in Jest will execute all tests

Currently, I have set up my testing framework using jest and ts-jest based on the guidelines provided by the ts-jest documentation. When I execute the command yarn test --listTests, I can identify the specific test file I intend to run: processNewUser.ts ...

What are the benefits of maintaining a property as non-observable instead of observable in knockout.js?

In my TypeScript project utilizing Knockout.js, I have a class with several properties. One of these properties is 'description', which is not directly tied to the DOM but needs to be used in popups triggered by certain mouse events (such as butt ...

Endpoint path for reverse matching in Mongodb API

I am currently in the process of setting up a webhook system that allows users to connect to any method on my express server by specifying a method and an endpoint to listen to (for example PUT /movies/*). This setup will then send the updated movie to the ...