Generate a complex string literal combination when the intermediary types or names are uncertain

My goal is to consolidate a group of nested string literals into one union.

The structure I am working with typically looks like this:

typechainTypes.factories.contracts
|
|----->Lock__factory (extends ContractFactory)
|           |-----> contractName: string
|----->****** (this is a `export * as foo from bar`)
            |-----> ****** (want to extract only if it extends ContractFactory; Extract<Foo, ContractFactory> type didn't work)
                      |-----> contractName: stringcontractName: string

This is what I envision in pseudocode:

type ContractFactories = typechainTypes.factories.contracts.**.* extends ContractFactory ? ___ : never;
type ContractNames = ContractFactories['contractName'];

In simpler terms:

type LookForFactory<T> = T.flatMap(foo => {
    if (foo extends ContractFactory)
        return foo;
    else if (foo === undefined || foo === null || foo is SomePrimitive)
        return never;
    else
        return LookForFactory<foo>;
}
type ContractNames = LookForFactory<typechainTypes.factories.contracts>;

The desired outcome should be a union consisting of these options:

typechainTypes.factories.contracts.Lock__factory.contractName
typechainTypes.factories.contracts.SomethingA.this_is_a__factory.contractName
typechainTypes.factories.contracts.SomethingB.SomethingC.this_is_a__factory.contractName
typechainTypes.factories.contracts.*.*.*.*.this_is_a__factory.contractName

If achieving an unknown depth is proving to be too complex, I would settle for a union of:

typechainTypes.factories.contracts.Lock__factory.contractName
typechainTypes.factories.contracts.UnknownNameA.UnknownNameB__factory.contractName

Initially, I attempted this using

Parameters<ethers.getContractFactory>[0]
, but the presence of any in the final overload led to any[], which was not helpful. Finding a way to exclude the any[] override remains a challenge.

Thank you!

Answer №1

It seems like what you're looking for is:

type FindFactory<T> = {
  [K in keyof T & string]:
    T[K] extends ContractFactory
      ? K
      : `${K}.${FindFactory<T[K]>}`
}[keyof T & string]

This ingenious code snippet defines a recursive conditional type.

  1. Firstly, it iterates over the keys of type T as K, considering only those that are strings.
  2. If the value at key K in type T is a ContractFactory, it returns that key.
  3. If the value at key K in type T is anything else, it merges the parent key K with all matches found in the nested object: ${K}.${FindFactory<T[K]>}.
  4. Finally, by using [keyof T & string] indexing, we obtain the value types of the newly created object as a union.

Let's put this into action with an example:

type ContractFactory = {
  contractName: string
}

type Typechain = { 
 // Nested objects representing contracts and factories
}

type FactoryNames = FindFactory<Typechain>;
/*
List of factory names extracted from the provided input.
*/

Explore Playground

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

Utilizing Angular's Dependency Injection to Provide Services to External Libraries

I'm currently developing an NPM package that enhances the functionalities of Material Datatable. One standout feature is the ability to specify a method that will be triggered when a user clicks on a specific cell. Here is how the property is defined ...

Vitek - Uncaught ReferenceError: Document Is Not Defined

Why am I encountering an error when trying to use File in my vitest code, even though I can typically use it anywhere else? How can I fix this issue? This is the content of my vite.config.ts. /// <reference types="vitest" /> import { defin ...

The information in the map cannot be inferred by Typescript based on the generic key type

I encountered a TypeScript issue while refactoring an existing feature in my app. It seems that TypeScript is having trouble picking up the correct type when passing the generic type through nested functions. enum App { AppOne = 'app-one', A ...

What causes the appearance of the "?" symbol at the beginning of the URL and triggers a reboot of the app when moving through the absolute path?

I am facing an issue. In my multi-module application with lazy loading, I encountered a strange behavior when trying to navigate between child lazy modules. Transition from top module to bottom child module works fine, but routing from one bottom child la ...

Tips for transferring information from a Sidemenu to a different page during navigation

Essentially, I have two types of users: Teachers and Students. How can I display different profile screens depending on the user type? The Sidemenu is located in app.components.ts file. ...

Setting a maximum limit for selections in MUI Autocomplete

Code updated to display the complete script logic. I want to restrict the number of selections based on a value retrieved from the database, but in this example, I have set it to 3 manually. The error message I'm encountering is "Cannot read properti ...

Creating objects in Angular 2 through HTTP GET calls

Recently, I've delved into learning Angular 2. My current challenge involves making http get requests to retrieve data and then constructing objects from that data for later display using templates. If you believe my approach is incorrect, please feel ...

NextJS is currently unable to identify and interpret TypeScript files

I am looking to build my website using TypeScript instead of JavaScript. I followed the NextJS official guide for installing TS from scratch, but when I execute npm run dev, a 404 Error page greets me. Okay, below is my tsconfig.json: { "compilerOption ...

Angular: Deciding Between Utilizing Boolean @Input and Attribute @Directive - What's the Best Approach?

My goal with Angular is to create a "directive" that can add functionality to my component, specifically adding a myPortlet with a close button when using the directive myHasCloseButton. <myPortlet myHasCloseButton>...</myPortlet> In explori ...

The ins and outs of Angular's type checking mechanisms

I have a few different scenarios on my mind. Imagine if I make an http call to fetch all movies from my php backend api. This is observable, so I need to subscribe to it. // Here's my service getAll() : Observable<Movie[]>{ this.http.get ...

MobX React not causing re-render when props change

Just diving into MobX and encountering some roadblocks while trying to call async actions. In my store, there's an async function responsible for updating an observable array: export class AccountStore implements IAccountStore { @observable accounts ...

Is it possible to convert a string using object-to-object syntax?

After running a particular function, I received the following results: [ "users[0].name is invalid", "date is invalid", "address.ZIP is invalid" ] I am looking for a way to convert this output from object syntax i ...

What could be causing my component to not refresh when used as a child?

I have been experimenting with some code to track rerenders. The initial approach failed when passing <MyComponent> as a child component. it("should return the same object after parent component rerenders", async () => { jest.useF ...

ERROR: Unhandled promise rejection: Route cannot be found. URL Segment: 'details'

My current setup involves a router configuration in my Angular application. Below is the code snippet showcasing my router settings: import { Route, RouterModule } from '@angular/router'; import { ProjectDetailsComponent } from '../componen ...

Dynamic row generation with dropdown menu and data binding

Currently, I am working with a table that dynamically creates rows containing details of uploaded files. Each row includes a dropdown menu for selecting the file type. The issue I am encountering is with the dynamically generated dropdown menus. If I sele ...

Error message encountered: Missing property status in TypeScript code

An error occurs in the refetchInterval when accessing data.status, with a message saying "property status does not exist" chatwrapper.tsx const ChatWrapper = ({ fileId }: ChatWrapperProps) => { const { data, isLoading } = trpc.getFileUploadStatus.use ...

Create a new TypeScript object without any initial properties, then proceed to define its attributes

Working on honing my skills with Angular Forms utilizing the template-driven approach. My goal is to construct a user interface to display the output of my data in this structure: <hr> <div class="well well-lg"> ...

typescript decorator implemented on a class that is nested within another class

Is it possible to decorate a nested property within a class? Let's explore with an example: function log(name: string = 'DecoratedProp') { return function logHandler(target: any, field: any) { // get the key ...

How can I verify the "Type" of user input in a custom React TypeScript hook?

Creating a customized hook to fetch JSON data from an API is a task I am currently working on. The challenge is that I intend to use this hook for various types of data, so I need a mechanism to specify the type each time I utilize the hook. Depending on t ...

Exploring TypeScript's Conditional Types

Consider this scenario: type TypeMapping = { Boolean: boolean, String: string, Number: number, ArrayOfString: Array<string>, ArrayOfBoolean: Array<boolean> } export interface ElemType { foo: keyof TypeMapping, default: valueof T ...