Troubles with Type Inference in Typescript Conditional Types

Explore the Unique Playground

Given a specific type:

export declare type CustomFilter<T> = {
  [P in keyof T]?: (P extends keyof T ? T[P] : any);
};

Encountering an issue when defining the filter as follows:

update.$setOnInsert.createdAt = new Date();

An error is thrown stating:

Type 'Date' is not assignable to type '"createdAt" extends keyof T ? T[keyof T & "createdAt"] : any'.ts(2322)

The problem disappears if the filter is changed to:

export declare type CustomFilter<T> = {
  [P in keyof T]?: any;
};

The question arises as to why the conditional statement does not deduce the correct type and instead retains the code portion.

Although there may be other issues with the filter structure, the core problem lies in conditional statements failing to ascertain the actual type.

Detailed Example

interface DocumentCollection {
  _id?: string;
  createdAt: Date;
}

type PathImpl<T, Key extends keyof T> =
  Key extends string
  ? T[Key] extends Record<string, any>
    ? `${Key}.${PathImpl<T[Key], Exclude<keyof T[Key], keyof Date | keyof Object | keyof string> & string>}`
      | `${Key}.${Exclude<keyof T[Key], keyof Date | keyof Object | keyof string> & string}`
    : never
  : never;

type Path<T> = keyof T | PathImpl<T, keyof T>;

type PathValue<T, P extends Path<T>> =
    P extends `${infer Key}.${infer Rest}`
    ? Key extends keyof T
      ? Rest extends Path<T[Key]>
        ? PathValue<T[Key], Rest>
        : never
      : never
    : P extends keyof T
      ? T[P]
      : any;

export declare type CustomFilter<T> = {
    [P in Path<T>]?: PathValue<T, P>;
};

export class CustomCollection<T extends DocumentCollection> {
  constructor() {}
  
  updateOne(filter: CustomFilter<T>) {
    // ISSUE OCCURS HERE
    filter._id = '';
    filter.createdAt = new Date();
  }
}

// THIS FUNCTION IS WORKING AS EXPECTED
let testModel = new CustomCollection<DocumentCollection>();
testModel.updateOne({_id: 'test', createdAt: new Date()});

Additional complexities have been added to the playground scenario leading to further similar challenges:

Navigate through the Enhanced Playground

A recursive type implementation seems to trigger this error:

Type instantiation is excessively deep and possibly infinite.ts(2589)

Answer №1

When it comes to the generic type T in the following code snippet:

export class MongoManagerCollection<T extends CollectionDocument> {
  constructor() { }

  updateOne(filter: MongoManagerFilter<T>) {
    // THIS DOES NOT WORK
    filter._id = '';
    filter.createdAt = new Date();
  }
}

it seems like a mystery. The value of filter._id is treated as an unresolved conditional type, and should be viewed as if it were an uncalled function. Just like an uncalled function returns no value, the same goes for filter._id.

type PathValue<T, P extends Path<T>> =
  P extends `${infer Key}.${infer Rest}`
  ? Key extends keyof T
  ? Rest extends Path<T[Key]>
  ? PathValue<T[Key], Rest>
  : never
  : never
  : P extends keyof T ////////////////////////////////////////////////
  ? T[P]              // Property is resolved as a conditional type //
  : any;              ///////////////////////////////////////////////

If you examine closely, filter._id corresponds to P extends keyof T ? T[P] : any, rather than just T[P].

An alternative approach exists. Instead of using a conditional type, consider using T[P & keyof T]. Here's an example:

interface CollectionDocument {
  _id?: string;
  createdAt: Date;
}

// More code blocks follow...

For a detailed explanation and examples about deep picking implementation in TypeScript, check out the links provided above.

Without the need for generics, the updated code snippet can look like this:

export class MongoManagerCollection {
  constructor() { }

  updateOne(filter: MongoManagerFilter<CollectionDocument>) {
    filter._id = ''; // ok
    filter.createdAt = new Date(); // ok

  }
}

// THIS WORKS
let testModel = new MongoManagerCollection();
testModel.updateOne({ _id: 'test', createdAt: new Date() });

Feel free to explore these suggestions and adapt them based on your specific MongoDB API requirements.

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

The CastError occurred because the attempt to add a string value to an array failed when converting it to a string

An issue I am encountering: *CastError: Cast to string failed for value "[ 'ZTM', 'NASA' ]" (type Array) at path "customers" at model.Query.exec (/Users/mike/Documents/NodeJS-applications/NASA-project/server/node_modules/mongoose/lib/qu ...

Incorporating Auth0 into NestJS for Enhanced Security on gRPC Endpoints

I have been working on implementing NestJS Guards for Authentication and Authorization in my gRPC Services built with NestJS. @GrpcMethod(USER_SERVICE_NAME, 'GetUser') private getUser(req: GetUserRequest): Promise<GetUserResponse> { ret ...

The shopping list feature is unable to save or list multiple recipes at once

My goal is to: Create a shopping list that retrieves recipes from an API. Transfer ingredients from one page to another. Automatically refresh/load data when more than 1 item is added. The challenge I am facing is: Only one set of ingredients loads. T ...

Instantiate a TypeScript object and establish its type by setting restrictions derived from an input object

I have been working on creating a function that takes an object A: { [key: string]: string | undefined } as its parameter. The goal is to generate a new object B with all properties from A, converting each string property to type number, and each string | ...

Angular/TypeScript restricts object literals to declaring properties that are known and defined

I received an error message: Type '{ quantity: number; }' is not assignable to type 'Partial<EditOrderConfirmModalComponent>'. Object literal may only specify known properties, and 'quantity' does not exist in type &ap ...

Encountering an issue with TypeScript and Jest when trying to import a file leads to an

Having trouble with using Jest in a TypeScript environment. //myprovider.tsx class MyProvider{ constructor(){} giveMeFive(): int{ return 5; } } export { MyProvider } // myprovider.test.js import { MyProvider } from './myprovider'; ...

Executing a function to erase the stored value in local storage during an Angular unit test

Looking to verify whether the localStorage gets cleared when I execute my function. Component ngOnInit() { // Logging out when reaching login screen for login purposes this.authService.logout(); } authService logout() { // Removing logged i ...

Sort columns in a MUI datatable

I am facing an issue with sorting in a column that represents an object. Although I can display the desired value, the sorting functionality does not seem to work for that particular column. Here is an example to provide better clarity: const [data, set ...

When incorporating Typescript into HTML, the text does not appear in bold as expected

Issue with bold functionality in Typescript Hey there, I am currently involved in an Angular project and I came across a problem with a function in a Typescript file that is supposed to bold a specific segment of text formatText() { ......... ...

Checking at compile time whether a TypeScript interface contains one or multiple properties

Is there a way to determine if a typescript interface contains at least one property at compile time without knowing the property names? For example, with the interfaces Cat and Dog defined as follows: export type Cat = {}; export type Dog = { barking: bo ...

Angular 4's OrderBy Directive for Sorting Results

I've been working on implementing a sorting pipe based on the code provided in this resource: The issue I'm facing revolves around handling undefined values within my data. The sorting pipe functions correctly when there are no undefined values ...

Using Typescript to define Vuex store types

Attempting to create a TypeScript-friendly Vuex store has been quite the challenge. Following instructions outlined here, I've encountered an issue where accessing this.$store from a component results in a type of Store<any>. I'm strugglin ...

Anticipating the completion of post requests

I am currently working on implementing a file upload feature in Angular. I have tackled the issue of file size restrictions by creating an API endpoint that can receive file chunks. Once all the chunks are received, another endpoint needs to be triggered ...

"Learn the steps for accessing the most recent version of a ReactJS application from the server

I have my react app hosted on a Netlify domain. How can I ensure that users who have previously loaded older versions of the app are redirected to the most recent deployed version? ...

Using TypeScript and NestJs: Spread types can only be generated from object types

I'm encountering an issue while trying to pass two parameters using the spread operator from the book.controller to the book.service.ts service. The error message I'm receiving is: Spread types may only be created from object types It's w ...

Encountered a hiccup with TSDX while trying to create a project using the react-with-storybook

My goal was to develop a UI components library using Storybook and React. This was my first time working with Storybook, so I followed the instructions provided in the documentation: I initiated the project by running npx tsdx create my-components in the ...

Tips on incorporating a child component into a parent component using React and TypeScript

I am trying to conditionally render a child component within a parent component using React and TypeScript. Here is the code I have: const Parent = ({ prop1, prop2 }: { prop1: Prop1, prop2: Prop2; }) => { const isChecked = true; return ( ...

Issue with Typescript: conditional return type failing to function

I am working with a function that has its return type determined by the arguments provided. const example = (flag: boolean): typeof flag extends true ? "yes" : "no" => { if (flag === true) { return "yes" } else { ...

Creating a null array of a specific size can easily be accomplished in Typescript

When I use the splice method to add elements to an array at a specified index, I find myself creating a null array first in order to achieve this. If I use an empty array instead, the elements do not get pushed to the specific instance that I intended. Cur ...

Is there a way to incorporate an "else" condition in a TypeScript implementation?

I am trying to add a condition for when there are no references, I want to display the message no data is available. Currently, I am working with ReactJS and TypeScript. How can I implement this check? <div className="overview-text"> < ...