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

Why does the playwright's onEnd() results not include the duration as specified in the documentation? What am I overlooking?

The built-in onEnd method can have a results object that is accessible within the function. According to the documentation here, this object should include the property duration, which represents the time in milliseconds. However, when I attempt to access ...

Issue with displaying data using a custom pure pipe and boolean in ngIf condition

For my current web project, I require a friendship/follow feature. There are two roles involved: admins and regular users. Regular users have the ability to follow other users, while admins do not possess this capability. When a user wishes to follow anot ...

Transform the date format in react.js using information provided by an API endpoint

I'm currently working on a project using React and TypeScript where I need to format the date retrieved from an API. I am able to map over the data and display it, but I'm struggling to convert it into a format like '22 June 2021'. The ...

Is there a way in typescript to transform empty strings into null values?

As a newcomer to TypeScript, I have been exploring it for some time now. I am working with two interfaces, one is fetching data from a database that I do not have control over and cannot guarantee the values returned are as expected. // Retrieved from the ...

Typescript decorator specifically designed for abstract generic Container class's child elements

Struggling with Typescript generics in my project, specifically with Typescript 2.6. My goal is to design a MobX store that implements a class decorator for basic authentication checks. This decorator should take a class type derived from the abstract gen ...

Transferring Cookies through FETCH API using a GET method from the client-side to the server-side

Struggling with a challenge here: Attempting to send a cookie via a GET request to determine if the user is logged in. The cookie is successfully transmitted to my browser and is visible in the developer tools. When I manually make a request through the UR ...

Steps for displaying detailed information about a single product on an Ecommerce page

Currently in the process of developing my Ecommerce project, I have successfully created a product grid with links to each specific product. However, I am facing an issue where I am unable to view the data of each individual item. Below is the code for my ...

Creating a Vue 3 Typescript project may lead to encountering the error message "this is undefined"

Just diving into Vue using Vite and TypeScript for my project, but running into errors during the build process. Most of them are Object is possibly 'undefined', particularly in parts of my template like this: <input :value="this.$store.s ...

Interactive feature on Google Maps information window allowing navigation to page's functions

Working on an Angular2 / Ionic 2 mobile App, I am utilizing the google maps JS API to display markers on a map. Upon clicking a marker, an information window pops up containing text and a button that triggers a function when clicked. If this function simpl ...

Learning to utilize the i18n library with React Vite

The developer console is showing the following message: i18next::translator: missingKey en translation suche suche Here is the file structure of my project: vite.config.ts i18n.js test/ src/ components/InputSearch.tsx routes/ public/ de/translation. ...

Is there a way for me to receive a unique selection of items from an array for every accordion item?

I am currently facing an issue where all three accordion items have the same set of objects, but I want each of them to have a different set. Here's what I have attempted so far, but it is not working as expected: const meetingRooms = [ { ...

What causes the error message "Expected ':' when utilizing null conditional in TypeScript?"

UPDATE: After receiving CodeCaster's comment, I realized the issue was due to me using TypeScript version 3.5 instead of 3.7+. It was surprising because these checks seemed to be working fine with other Angular elements, such as <div *ngIf="pa ...

The browser is not displaying the HTML correctly for the Polymer Paper-Menu component

I attempted to implement a paper-menu, but I am facing issues with the rendered HTML and its interaction. When I click on a menu item, the entire list disappears due to the paper-item elements not being properly placed inside a key div within the paper-men ...

WebStorm's TypeScript definitions are failing to function properly

I'm experiencing an issue with my three.js code and TypeScript definitions in settings. Despite enabling them, there doesn't seem to be any effect. I've downloaded everything and checked the necessary boxes, but nothing is changing. WebStorm ...

Astro component experiencing script tag malfunction within content collection

I'm trying to create something using a script tag, but for some reason the script doesn't seem to be working properly. Below is my content collection: --- title: Essay Similarity stakeholder: THESIS articleDate: 05 Feb 2023 projectStart: 2022-08 ...

Is it possible to prevent casting to any by improving type definitions?

My query can be best elucidated with the following code snippet. The comments within the code aim to provide clarity on the nature of the question. type MediaFormats = "video" | "audio"; type IMediaContent<TType extends MediaFormats, TValue> = { ...

Create a union type by utilizing indices of an array type

For instance: type BasicTheme = { name: 'basic'; colors: [string, string]; }; type AdvancedTheme = { name: 'advanced'; colors: [string, string, string, string]; }; type MainColor = ???; // 'main-1' | 'main-2&apo ...

"TypeScript function returning a boolean value upon completion of a resolved promise

When working on a promise that returns a boolean in TypeScript, I encountered an error message that says: A 'get' accessor must return a value. The code snippet causing the issue is as follows: get tokenValid(): boolean { // Check if curre ...

Vee-Validate: Are flags on the field value yielding undefined results? Explained with TypeScript

The documentation states that by using a flag on the value of a field, I should be able to obtain a boolean. For example: computed: { isFormDirty() { return Object.keys(this.fields).some(key => this.fields[key].dirty); } }, I am working ...

Using Angular 4 to transfer data from a dynamic modal to a component

Currently implementing material design, I have set up a dialogService for dynamically loading MdDialog. My goal is to create a search dialog with filters that, upon submission, directs the user to a search-results component route. However, I am struggling ...