Typescript Factory Creation by Leveraging infer and typeof

Primary Objective: My main focus is to grasp the concept of creating a Factory in Typescript, not just copying and pasting code. I am specifically struggling with understanding types and type inference. My goal is to have a "MyCloner" class that can generate multiple instances of an object that implements the IClonable interface, such as Truck or Motorcycle.

I envision being able to do something like this:

const vehicleCloner = new MyCloner();
const truck = new Truck({color: 'red', fuel: 'electric'});

And then use MyCloner to achieve the following:

var myTenElectricTrucks = vehicleCloner.cloneWithRandomColors(truck, 10);

First Area of Confusion (infer and new): I've been studying various tutorials, but one particular section has me puzzled:

type ExtractInstanceType<T> = T extends new () => infer R ? R : never;

The syntax here is throwing me off. It seems we are defining a type with a generic called T, which seems to extend the "new" function keyword. How are R, T, and 'new' connected? I'm also unsure about the purpose of the => operator and what infer does in Typescript.

If you're interested, you can find more information on ExtractInstanceType in this tutorial.

I acknowledge that keeping all trucks electric might be an additional feature not commonly seen in the Factory pattern, but grasping infer and ExtractInstanceType fundamentals should pave the way for achieving this ultimate objective.

Second Area of Confusion (type declarations & literals):

Another line from the same tutorial is causing confusion:

type userTypes = typeof userMap[Keys]; //typeof Developer | typeof Manager

To me, it appears that Keys isn't just a single key. Normally in JavaScript, I would expect it to be a string retrieving a single value from a dictionary. However, Keys seems to represent multiple types as a type literal, which is then utilized as a single key somehow?

You can refer to Keys below:

type Keys = keyof typeof userMap; // 'dev' | 'manager'

Answer №1

It is recommended that you ask one question per inquiry in the future, but your answers are concise and clear.

Let's convert the conditional type declaration into pseudocode for better understanding:

The instance type of T is defined as follows: if T is a class, then it represents the type constructed by instantiating that class; otherwise, it is 'never'.

We can rewrite this in different ways to represent the same concept:

type InstanceType<T> = (T is a class) ? (thing-T-constructs, i.e. 'R') : never
type InstanceType<T> = (T extends (new () => R)) ? R : never

By breaking it down step by step, we can simplify and understand the code better. The usage of 'T extends new () => R' denotes that 'T' satisfies the constraint of being a constructible entity returning an 'R', with 'R' representing what the class 'T' constructs. The inclusion of 'never' acts as a precaution against misuse of non-class generic parameters.

Although structurally valid Typescript is almost achieved, the unresolved variable 'R' poses an issue both in code and types. This is where the 'infer' keyword comes into play:

type InstanceType<T> = (T extends (new () => infer R)) ? R : never
// Simplified version:
type InstanceType<T> = T extends new () => infer R ? R : never

With 'infer', we instruct the compiler to deduce the type of 'R' based on the fulfilled condition - that 'T' is a class constructible using 'new'. Notably, 'infer' can only be utilized within conditional types like this.

type Keys = keyof typeof userMap;

In this scenario, we aim to create a type derived from the keys in 'userMap'. Since values cannot be used as types directly, we utilize 'typeof' to extract the type of 'userMap' and 'keyof' to acquire the union of map keys: 'dev' | 'manager'.

Subsequently, employing an index type enables us to gather the union of value types (in terms of key/value pairs) from the map:

type userTypes = typeof userMap[Keys];

While one might expect 'Developer | Manager', since we're forming a type ('userTypes') and unable to apply values as types, the actual union becomes 'typeof Developer | typeof Manager'.

To summarize the latter portion:

typeof userMap;       // Represents the compile-time type of userMap
keyof typeof userMap; // Indicates the type of *compile-time* keys in userMap
typeof userMap[keyof typeof userMap] // Same as above but for the types of *values* in the map

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

Issue with Adding Additional Property to react-leaflet Marker Component in TypeScript

I'm attempting to include an extra property count in the Marker component provided by react-leaflet. Unfortunately, we're encountering an error. Type '{ children: Element; position: [number, number]; key: number; count: number; }' is n ...

React/Ionic: Avoiding SVG rendering using <img/> elements

I seem to be encountering an issue when trying to load SVG's in my React/Ionic App. I am fetching weather data from OpenWeatherMap and using the weather?.weather[0].icon property to determine which icon to display. I am utilizing icons from the follow ...

What is the significance of requiring a specific string in a Typescript Record when it is combined with a primitive type in a union?

I am facing an issue with the following data type: type ErrorMessages = Record<number | 'default', string>; When I declare a variable like const text: ErrorMessages = {403: 'forbidden'}, Typescript points out that default is miss ...

Optimizing Text Formatting for Angular: Learn how to seamlessly showcase rich content in its proper format

I have a paragraph content fetched from a strapi endpoint that is in rich text format. When displayed on the page using a p tag, it shows with all the rich text formats such as ** for bold and more. How can I display it correctly? Inside the .ts file, // ...

import component dynamically from object in Next.js

Currently, I have a collection of components that I am aiming to dynamically import using next/dynamic. I'm curious if this is achievable. Here's the object in interest: // IconComponents.tsx import { Tick, Star } from 'components ...

What is the method for importing a JavaScript file into my TypeScript file?

Recently, I've encountered an issue while working with Typescript and angular 2. I have built an EncryptionService that looks like this: import {Injectable} from 'angular2/core'; import './lib/hmac256-enc64'; @Injectable() ...

Can the discriminator be preprocessed in a zod discriminated union?

Is it possible to convert string formatted numbers to numbers before using a value in z.discriminatedUnion? Here is an example code snippet: import { z } from "zod"; const BrushColorEnum = z.enum( ["BLUE_SILVER", "GREEN_SILVER&q ...

Shift the Kid Element to an Alternate Holder

Currently, I am working on a project in Angular version 10. Within this app, there is a component that can be shared and will utilize the provided content through ng-content. Typically, this content will consist of a list of items such as divs or buttons. ...

Incorporating regular expressions to extract a specific string from a URL is a requirement

Can anyone assist with extracting a specific string using regex in TypeScript? I have the following URL: https://test.io/content/storage/id/urn:aaid:sc:US:8eda16d4-baba-4c90-84ca-0f4c215358a1;revision=0?component_id=e62a5567-066d-452a-b147-19d909396132 I ...

After a cell editing event, there are times when the grid data is not saved properly due to

I have integrated the ag-grid library into my project for data display. After editing a cell, I want to save the changes to the backend database by persisting the rowData. Most of the time, this process works smoothly, but occasionally I encounter an issue ...

Ways to transfer an Object from a service to a component

I'm currently working on my website and trying to implement a cart feature where users can add items. To achieve this, I have created a service that contains the cart as an object called cart. The service has functions to add items to the cart and ret ...

Angular jsonp.get request was denied despite receiving a status code of 200 indicating success

I have been attempting to access my basic web API created in Jersey, which returns the following data: [ { "id": 1, "name": "Facebook", "userName": "<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="f4 ...

How can I use appendChild to place two different elements into a single div?

While exploring similar questions on this topic, I have unfortunately not come across a solution that works for me. My challenge is trying to insert two a elements inside of a newly created div element using appendChild. However, I am unable to append them ...

Date selection feature in Material UI causing application malfunction when using defaultValue attribute with Typescript

Recently, I discovered the amazing functionality of the Material UI library and decided to try out their date pickers. Everything seemed fine at first, but now I'm facing an issue that has left me puzzled. Below is a snippet of my code (which closely ...

Conceal the initial value in a dropdown menu in a React component

I've set up a codesandbox to demonstrate the issue (https://codesandbox.io/s/practical-flower-k6cyl?file=/src/App.tsx) Is there a way to prevent the "AGE" text (first option) in the select box from being selected again? It should only be visible when ...

What is the most effective way to share data among components in React?

I recently delved into learning about react and find myself puzzled on how to pass data between two components. Presently, I have set up 2 functions in the following manner: First, there's topbar.tsx which displays information for the top bar, inclu ...

Typescript: Utilizing the new keyword within a namespace

I'm currently working on developing DefinitelyTyped for a private package where I am unable to modify the source code. I am facing challenges in implementing a type structure like this: GlobalNameSpace.SuperClass = function(arg) {} GlobalNameSpac ...

Comparing two arrays in Angular through filtering

I have two arrays and I am trying to display only the data that matches with the first array. For example: The first array looks like this: ["1", "2" , "3"] The second array is as follows: [{"name": "xyz", "id": "1"},{"name":"abc", "id": "3"}, ,{"name ...

Ways to retrieve and bind data using onMounted in VueJS

Loading Data in Test.vue Component <template> <li v-for="item in masterCompany" v-bind:key="item.id"> {{ item.displayName }} </li> </template> <script> import Test from "../hooks/Test.hook" ...

Modify the VUE component within the module prior to its loading

I have a component structured like this: <template> <div> <some-module /> </div> </template> <script lang="ts"> import { Vue, Component, Prop } from 'vue-property-decorator'; import SomeMo ...