Exract nested type from an array of generic types in TypeScript

Objective: Retrieve the type from a neighboring field or function parameter. The type of this field/parameter is then stored in an array and treated as a generic.

class Resource<T> {
    value: T;

    constructor(
        dependencies?: Resource<any>[],
        initFunction?: ((dependencyValues: any[]) => void)
    ) {
        // ...
    }
}

const dep1 = new Resource<string>();
const dep2 = new Resource<number>();

const r = new Resource<boolean>(
    [dep1, dep2],
    (deps) => {
        // Current scenario:
        //  deps: any[]

        // Desired outcome with real types:
        //  deps: [string, number]
    }
);

The same objective can be approached differently by using a configuration object instead of constructor parameters.

class Resource<T> {
    constructor(config?: ResourceConfig<T>) {
        // ...
    }
}

interface ResourceConfig<T> {
    dependencies?: Resource<any>[];
    init?: ((dependencyValues: any[]) => void);
}

So, how can this be achieved?

P.S. While I could manually specify the type of parameters like deps: [string, number], my goal is to automatically obtain the required types without manual intervention every time.

I have tried various approaches, including inferring types, but have been met with challenges where the preprocessor still defaults to any or unknown for these fields.

Answer №1

Add an additional type parameter to the Resource class and set its default value to []:

class Resource<T, Deps extends Resource<any>[] = []> {

Now, you will see that it is no longer possible to include dependencies without explicitly specifying this second type parameter:

new Resource<string>([dep1, dep2]); // errors
//                    ~~~~ Type 'Resource<string, []>' is not assignable to type 'undefined'.(2322)

This error occurs because the second parameter has not been provided and defaults to []. To work around this limitation due to the absence of partial type inference, you can create a curried static factory function that forwards its parameters and type parameters to the constructor:

static create<T>() {
    return <Deps extends Resource<any>[] = []>(
        ...args: ConstructorParameters<typeof Resource<T, Deps>>
    ) => new Resource<T, Deps>(...args);
}

With this approach, the constructor can now utilize the Deps parameter and provide the correct types for the callback function:

/** @internal */
constructor(
    dependencies?: [...Deps],
    initFunction?: (dependencyValues: {
        [K in keyof Deps]: Deps[K] extends Resource<infer T> ? T : never;
    }) => void
) {}

By inferring Deps as a tuple using variadic tuple types inside the constructor and mapping over it with a mapped type, we can ascertain the resource's value type.

You can now utilize this functionality as demonstrated below:

const dep1 = Resource.create<string>()();
const dep2 = Resource.create<number>()();

const r = Resource.create<boolean>()([dep1, dep2], ([str, num]) => {
    str;
    //  ^? string
    num;
    //  ^? number
});

If there is no need to pass dependencies, you can still use the traditional constructor syntax:

const dep1 = new Resource<string>();
const dep2 = new Resource<string>();

Explore this code snippet further on the 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

Error: Unable to access properties from an undefined source while trying to read 'reRenderOnLangChange'

I encountered an issue in my Angular project where my unit tests are failing with the following error message: TypeError: Cannot read properties of undefined (reading 'reRenderOnLangChange') at shouldListenToLangChanges (node_modules/@ngneat/tr ...

Property element does not exist in this basic TypeScript project

I'm diving into my initial TypeScript project and encountering an issue with the "style". I attempted to utilize style!, create an if(changeBackgroundColor){}, but without success. let changeBackgroundColor = document.querySelectorAll('[data-styl ...

What is the reason for Typescript not throwing an error when arguments are missing?

Here is my code snippet: type Callback = (a: string, b: string) => void const observer: Callback = function(a: string): void { console.log(a) } observer('foo') I am encountering an issue with Typescript on the last line: Expected 2 argu ...

In the realm of custom libraries, it is impermissible to declare an accessor within an ambient context

I am working on a project with webpack and TypeScript (Project A) that generates a small library. This library is then utilized in an Angular 8 project (Project B). In Project A, I define a class as follows: export class Foo { private foo: s ...

Enhancing the default functionality of React.FC within Next.js

Currently, I am working on a tutorial in Nextjs that employs the code snippet below in JavaScript. However, I am planning to transition it to TypeScript. Since I am relatively new to TypeScript, I have attempted various solutions from different sources but ...

Exploring Nested JSON Iteration in Angular4

I am struggling to iterate through a nested JSON and extract the desired output. Can someone assist me in retrieving the bpmn:startEvent id value from the JSON provided below? { "bpmn:definitions":{ "@attributes":{ "xmlns:xsi":"h ...

Utilizing TypeScript Generics for Union Type Props in React Development

In the snippet below, an error is encountered on the line props.onChange(e.target.value as typeof props.value): "Argument of type 'string | string[]' is not assignable to parameter of type 'string & string[]'." e.target.value is id ...

I am attempting to gather user input for an array while also ensuring that duplicate values are being checked

Can someone assist me with the following issue: https://stackblitz.com/edit/duplicates-aas5zs?file=app%2Fapp.component.ts,app%2Fapp.component.html I am having trouble finding duplicate values and displaying them below. Any guidance would be appreciated. I ...

Tips for resolving the TypeScript error related to the global object in Node.js

I am currently working on a project following the steps outlined in this guide https://vercel.com/guides/nextjs-prisma-postgres to develop a full stack application. However, I have encountered an error with TypeScript in the code snippet below: import { Pr ...

Combining two elements in Angular 2

I am looking to find the intersection of two objects. My goal is to compare these objects and if they have matching values on corresponding keys, then I want to add them to a new object. obj1 = { "Projects": [ "test" ], "Companies": [ "facebook", "google ...

Experiencing difficulty in personalizing VueDatePicker

<template> <VueDatePicker v-model="date" ref="datepicker" /> </template> <script setup> import { ref } from 'vue'; const date = ref(); const datepicker = ref(null); const yourCustomMethod = () =&g ...

What is the correct way to handle Vue props that include a dash in their name?

I am currently working on a project using Vue. Following the guidelines of eslint, I am restricted from naming props in camel case. If I try to do so, it triggers a warning saying Attribute ':clientId' must be hyphenated. eslint vue/attribute-hyp ...

How can I replicate a div in Angular 6 using typescript?

I'm currently working on a project focused on providing detailed information about various vehicle components. Each component consists of specific descriptive fields. One feature I'm looking to implement is an "Add another component" button that ...

Issues with the messaging functionality of socket.io

Utilizing socket.io and socket.io-client, I have set up a chat system for users and operators. The connections are established successfully, but I am encountering strange behavior when it comes to sending messages. For instance, when I send a message from ...

Exploring the world of functional programming in Java can be a rewarding experience, especially

I am seeking a method to define generic computation on a data set and have the compiler alert me if there are any errors. Having experience with TypeScript, I have seen that you can achieve something like this: /** * Type inferred as: * Array<{ * ...

Transferring a JSON file between components within Angular 6 utilizing a service

I have been facing an issue in passing the response obtained from http.get() in the displayresults component to the articleinfo component. Initially, I used queryParams for this purpose but realized that I need to pass more complex data from my JSON which ...

Is it possible to add a value to a different entity in TypeORM?

I currently have the code below in my project; @Entity() export class User { @PrimaryGeneratedColumn() id!: number @Column() name: string } If I were to add a new User like this: {name: "Kahvi", gold: "200", exp: "500"} How can I implement a ...

How to format a date in ngModel using Angular 9 and above

Is there a way to format the date displayed in my input using ngModel to follow a specific format like 'MM/dd/YYYY'? Can this be achieved by using ngModel? For example, could we do something like model[(ngModel)]="data.targetDate | date:'MM ...

Is it possible to use TypeScript in a React Native project with a JavaScript file?

Currently, I am learning React Native by working on app clones like Instagram and YouTube. I have recently started an AirBnb clone project, but I'm facing some issues with the initial build. One issue I noticed is that in 'App.js', the temp ...

Enhancing Type Safety with TypeScript in 2020: A User-Friendly Approach

It is widely understood that TypeScript conducts type-checking solely at compile-time. While there are existing methods, like io-ts, to incorporate runtime checks, I can't help but wonder if a more straightforward approach exists. For instance, cons ...