Issue with TypeScript: Difficulty accessing keys in a recursive manner

I've created a custom type that eliminates any nullish values when working with objects.

export type ExcludeNullish<T> = Exclude<T, null | undefined>;
export type ExcludeNullishKeys<T> = {
    [K in keyof T]-?: T[K] extends boolean | string | number | symbol ? ExcludeNullish<T[K]> : ExcludeNullishKeys<T[K]>;
};

However, I encountered an issue while trying to access a nested key using TypeScript:

export type CustomObj = ExcludeNullishKeys<{
    parentKey: {
        childKey?: {
            data: 'not available';
        } | null;
    } | null;
}>;
export type AccessingNestedObject = CustomObj['parentKey']['childKey'];

The error message states:

Property 'childKey' does not exist on type 'ExcludeNullishKeys<{ childKey?: { data: "not available"; } | null | undefined; } | null>'.

It seems like the compiler is treating the resulting type as ExcludeNullishKeys rather than recognizing it as an object without any nullish values. Any insights on this behavior?

Answer №1

By implementing a supporting type to expand the type of RandomObj, it becomes evident that there are flaws in your approach within the OmitNullishKeys function. The resulting type of RandomObj still includes values that could potentially be null.

type ExpandType<T> = T extends object
  ? T extends infer O ? { [K in keyof O]: ExpandType<O[K]> } : never
  : T;

type ExpandedRandomObj = ExpandType<RandomObj>;
/*
type ExpandedRandomObj = {
    stackOverflow: {
        forums: {
            thread1: 'not available';
        } | null;
    } | null;
}
*/

TS playground

Answer №2

The issue was initially resolved by:

export type N = null | undefined;
export type OmitNullish<T> = T extends (infer U)[] ? Exclude<U, N>[] : Exclude<U,N>
export type OmitNullishKeys<T> = {
  [key in keyof Required<T>]-?: T extends Record<string, unknown> ? OmitNullish<OmitNullishKeys<T[K]>> : OmitNullish<T[K]>
}

This action effectively resolved the issue

  1. The exclusion of nullish values was not being applied to objects due to the absence of OmitNullish
  2. Using keyof without specifying non-optionality resulted in a value of T | undefined. The use of Required ensures that the key is treated as mandatory.

although it did enforce the non-nullish rules for classes like Date.

By implementing an alternative solution presented by @jcalz and adding a conditional statement to prevent class overwrites, we obtain

type OmitNullish<T, E = Date> = T extends E ? T : NonNullable<{ 
  [key in keyof T]-?: OmitNullish<T[K]>
}>

this offers a concise method to ensure all elements within an object are non-nullish.

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

Is there a way to dynamically define the return type of a function in Typescript?

Can the variable baz be dynamically assigned the string type? type sampleType = () => ReturnType<sampleType>; // Want to return the type of any function I pass (Eg. ReturnType<typeof foo>) interface ISampleInterface { baz: sampleType; } ...

Error in Typescript: 'SyncClient' not found in Twilio

While working on my Ionic app, I encountered an issue every time I attempted to use the twilio-chat library in my project through npm install. The error consistently appeared in the .d.ts files. Here is how I imported it in my provider : import { Client ...

API endpoint generating a Vue component as a rendered output

In the process of developing a document templater service, I am faced with the challenge of handling numerous document templates (contracts, protocols, etc.) written in Vue. The concept revolves around clients sending props in the body, which are then pass ...

Utilizing React Typescript Discriminating Unions to choose between two different types based solely on props

In my project, I have a component that consists of different types: type Base = { color: string } type Button = { to: string } & Base type Link = { link: string linkNewTab: boolean } & Base type ComponentProps = Button | Link e ...

Error: Unable to Locate Module (Typescript with baseUrl Configuration)

Struggling to implement custom paths in my TypeScript project, I keep encountering the "webpackMissingModule" error due to webpack not recognizing my modules. I've attempted various solutions without any success. Any suggestions or ideas? Some packa ...

How to dynamically generate Angular component selectors with variables or loops?

Looking to dynamically generate the Selector Tag in my app.component.html using a variable. Let's say the variable name is: componentVar:string What I want in my app.component.html: <componentVar></componentVar> or <app-componentVar& ...

Unexplained Reference Error in Next.js Typescript: Variable Accessed before Initialization

I am currently working on an admin website and encountered the error Block-scoped variable used before its declaration.. I will provide details using images and code. This is my first time seeking help on StackOverflow. Error Message: Block-scoped variab ...

What is the best way to depict object key replacements within a Typescript definition?

I currently have these types: type PossibleKeys = number | string | symbol; type ValueOf<T extends object> = T[keyof T]; type ReplaceKeys<T extends Record<PossibleKeys, any>, U extends Partial<Record<keyof T, PossibleKeys>> = ...

Achieving JSX rendering in Vue.js with TypeScript starting from a basic CLI setup along with the JSX package integration

The Setup I have set up a new project using the vue-cli, where I manually selected certain features including Babel, TypeScript, Vuex, and Linter / Formatter. Additionally, I chose version 2.x and opted to use Babel alongside TypeScript for modern mode an ...

Utilizing Pipes within a Method in Angular 2 along with Dependency Injection triggers an "Insufficient Number of Arguments" error

I am searching for a solution to incorporate a custom pipe into my class. The custom pipe itself ( referenced from this source, many thanks ) involves injecting a dependency (the DomSanitizationService). import { Pipe, Inject, Injectable } from '@ang ...

Tips for resolving the error message "Nextjs with Typescript: 'describe' is not defined"

I am facing some obstacles while trying to compile my Nextjs project for production. Here is the list of errors that I encountered: ./components/Layout/Header/Header.test.tsx 6:1 Error: 'describe' is not defined. no-undef 7:20 Error: 'jes ...

Global variable appears undefined in Angular 2 view, yet it is still displaying

I'm running into issues with my Angular 2 code. Inside my ts file, I have the following: import { Test } from '../../../../models/test'; import { TestService } from '../../../../services/test.service'; import { Job} fr ...

Converting Getters into JSON

I am working with a sequelize model named User that has a getter field: public get isExternalUser(): boolean { return this.externalLogins.length > 0; } After fetching the User from the database, I noticed in the debugger that the isExternalUser prop ...

Odd behavior of escape characters in Typescript

Looking for help with a query similar to the one referenced here. I am new to TypeScript and front end development. Currently using an Angular form to collect user input, which may contain regex. For example: The input from the form, stored in this.expr ...

Tips for eliminating the gap between digits and symbols in an OutlinedTextField within the Material Ui framework

Using material Ui OutlinedTextField with the code snippet below import { List, styled, Switch, TextField, Theme, withStyles } from '@material-ui/core'; export const OutlinedTextField = withStyles((theme: Theme) => ({ root: { '& ...

If an interface property is set as (), what significance does it hold?

While exploring the Vue.js source code located at packages/reactivity/src/effects.ts, I came across this snippet: export interface ReactiveEffectRunner<T = any> { (): T effect: ReactiveEffect } I'm curious, what does () signify in the code ...

Error message: "react-router typescript navigation version 0.13.3 - Unable to access 'router' property"

I'm currently in the process of porting an existing React project to TypeScript. Everything seems to be going smoothly, except for the Navigation mixin for react-router (version 0.13.3). I keep encountering an error message that says "Cannot read prop ...

Angular File Upload Button Tutorial

English is not my first language, so please excuse any mistakes. I recently started learning Angular and I'm attempting to build a file upload button that lets users upload files based on dropdown menu options (such as USA States). Once uploaded, the ...

Incorporating an external SVG file into an Angular project and enhancing a particular SVG element within the SVG with an Angular Material Tooltip, all from a TypeScript file

Parts of the angular code that are specific |SVG File| <svg version="1.0" xmlns="http://www.w3.org/2000/svg" width="950" height="450" viewBox="0 0 1280.000000 1119.000000" preserveAspectRatio= ...

The type 'RefObject<HTMLDivElement>' cannot be matched with type 'RefObject<HTMLInputElement>' in this context

Encountered an error message: Issue with assigning types: Type '{ placeholder: string | undefined; autoComplete: string | undefined; "data-testid": string | undefined; onChange?: ChangeEventHandler<HTMLInputElement | HTMLTextAreaElement&g ...