TS2345 persists through unimaginable routes

export interface MyDocument{
  field: string;
}

function addNewDocument(doc: MyDocument) { /* database insertion logic goes here */ }

let placeholderValue; // emulating undefined value
let newDoc = { field: placeholderValue };

console.log(JSON.stringify(newDoc));
if (placeholderValue === undefined) { // this condition won't prevent if doc.field is undefined
  return; // exit function when placeholderValue is undefined
}

addNewDocument(newDoc); // triggers TS2345: Argument of type error

As far as I see it, this might not be achievable, but it seems like a hint of bad code. How do I notify TypeScript's type-checker that newDoc should never have a field with a value of undefined, even though it may occur for other reasons (like logging)?

Answer №1

Most of the narrowing behavior demonstrated by TypeScript primarily affects the value being checked. In other words, while an operation on expression exp1 could change the apparent type of exp1, it typically won't alter the apparent type of a different expression like exp2.

There are exceptions to this rule: inspecting the discriminant property of a discriminated union object will narrow the type of the object itself; you can save the result of a type guard check as a boolean and later use that value to further narrow the original expression; and destructuring discriminated unions into separate variables allows for one variable's check to narrow the other.

However, your situation does not fall into any of these exceptions. The language mechanisms do not propagate a check on x to impact the apparent type of doc. Even if future updates implement broader property type guards or expand specific features, they still wouldn't achieve the behavior you desire. Enabling such functionality would involve extensive counterfactual analysis by the compiler, making it impractical.

So, what alternatives can you consider?


If restructuring your code is not ideal, you have the option to utilize a type assertion to explicitly inform the compiler that doc should be treated as MyDoc:

if (x === undefined) throw new Error();
insert(doc as MyDoc); // assertion

This approach eliminates errors at the cost of manual verification for type safety. For instance, there won't be an error even if an incorrect check is performed:

if (x !== undefined) throw new Error(); // mistake
insert(doc as MyDoc); // assertion remains valid

Hence, exercise caution with this method.


If your goal is to align the compiler's reasoning with your logic, refactoring using supported narrowing techniques is necessary. One effective strategy involves creating a user-defined type guard function to detail the narrowing process to the compiler. While still limited to the checked value, it allows for complex typings to be expressed.

For example, you could define a function to validate whether a value represents a valid MyDoc:

function isMyDoc(x: any): x is MyDoc {
  return x && ("field" in x) && (typeof x.field === "string");
}

The compiler doesn't verify the inner workings of this function, but it facilitates its usage elsewhere:

if (!isMyDoc(doc)) throw new Error();
insert(doc); // permissible

Alternatively, you could develop a more generic guard that checks for a defined property at a specific key within an object:

function hasDefinedProp<T extends object, K extends keyof T>(
  obj: T, key: K): obj is T & { [P in K]-?: Exclude<T[K], undefined> } {
  return typeof obj[key] !== "undefined"
}

Although more intricate, this typing proves useful:

if (!hasDefinedProp(doc, "field")) throw new Error();
doc;
// { field: string | undefined } & { field: string }
insert(doc); // permissible

In this scenario, doc transitions from { field: string | undefined } to

{ field: string | undefined } & { field: string}
, thereby aligning with MyDoc. Depending on the frequency of such checks in your codebase, the versatility of hasDefinedProp() may overshadow that of isMyDoc().


To enhance clarity and maintain clean code, consider reorganizing your code to precede aliasing operations with the relevant checks:

const x = Math.random() < 0.5 ? "abc" : undefined;
if (x === undefined) throw new Error(); // establish guard first
let doc = { field: x }; // then proceed
insert(doc); // permissible

Though not always feasible, this refactoring ensures a straightforward understanding of the sequence of type guards by both the compiler and developers.

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

Encountering `error TS2554: Constructor expected 0 arguments, received 1` with ts-jest

I've recently delved back into TypeScript and started implementing TDD. I successfully set up ts-jest to run my tests, but I've hit a roadblock with a seemingly simple issue that has me stumped. organization.ts: class Organization implements IO ...

How to configure VSCode for automatically transpiling TypeScript and launching NodeJS debugger with just one press of the F5 key?

Working with VSCode+TypeScript+NodeJS can feel overly complex compared to a C# project in Visual Studio. Just hitting F5 in C# compiles the project and initiates debugging. How can I configure VSCode to do the same for TypeScript+NodeJS? What am I missing ...

Request denied due to CORS policy, despite setting Access-Control-Allow-Origin to *

My console is showing an error when I try to make a POST request on my website. The error states: Access to XMLHttpRequest at 'https://exp.mysite.com/i_l' from origin 'https//frontend.mysite.com' has been blocked by CORS policy: Respons ...

Converting milliseconds into days, hours, minutes, and seconds using Angular

Currently, I am attempting to convert milliseconds to the format dd:hh:mm:ss. For example, given 206000 milliseconds. The desired format for this value would be: 00:00:03:26. However, when utilizing the following code: showTimeWithHour(milliSeconds: numb ...

In Angular 2, templates may not be fully executed when utilizing ngAfterContentInit or ngAfterViewInit functions

When I try to access the DOM of the document, I notice that the template is only partially executed. Using a setTimeout helps me bypass the issue temporarily, but what is the correct way to handle this? import { Component, Input, AfterContentInit } from & ...

Opening and closing a default Bootstrap modal in Angular 2

Instead of using angular2-bootstrap or ng2-bs3-modal as recommended in the discussions on Angular 2 Bootstrap Modal and Angular 2.0 and Modal Dialog, I want to explore other options. I understand how the Bootstrap modal opens and closes: The display pro ...

The update query in TypeORM cannot be executed due to the absence of defined update values. To resolve this, make sure to call the "qb.set(...)" method to specify the values that

In my development stack, I am utilizing a MySQL database along with TypeORM and ExpressJS. I have established two entities: User and Client, which are connected by a one-to-one relationship with the Client holding a foreign key. Upon attempting to save a ...

Sequelize error: An unknown column is encountered in the field list while including a model without specifying the `attributes` field

Utilizing Sequelize for executing MySQL queries in my codebase, I have meticulously defined Models and connected them with their associations. Creating a music playlist structure with: Artists who can have multiple songs. Playlists that contain multiple ...

Using promises in TypeScript index signature

Can you help me find the correct index signature for this particular class? class MyClass { [index: string]: Promise<void> | Promise<MyType>; // not working public async methodOne (): Promise<void> { ... } public async methodTwo () ...

Troubleshooting issue with absolute paths in Vite project using React and TypeScript

I'm having trouble implementing absolute paths in a Vite react-ts project. This is how I set up the project: npm init @vitejs/app npx: installed 6 in 1.883s √ Project name: ... test-vite √ Select a framework: » react √ Select a variant: » rea ...

What is the solution to the error message "Expression generates a union type that is too intricate to encapsulate. ts(2590)"?

I am encountering an issue while using the Button component from the npm library @chakra-ui/react. When I try to use the Button element, it throws the following error: TypeScript: Expression results in a union type that is too complex to represent. ts( ...

Unable to simulate a static method in Vitest - encountering an error stating "is not a function"

I am currently writing unit tests using the Vitest framework for my TypeScript and React application, but I have encountered an issue with mocking static methods. Below is a snippet of my code: export class Person { private age: number; constructor(p ...

Error: TypeScript unable to locate file named "Image"

I'm encountering an issue while attempting to construct a class for loading images. The error message states that name "Image" not found within the array definition, even though I create an image object later in the code. class ImageLoad ...

Tips for uploading images, like photos, to an iOS application using Appium

I am a beginner in the world of appium automation. Currently, I am attempting to automate an iOS native app using the following stack: appium-webdriverio-javascript-jasmine. Here is some information about my environment: Appium Desktop APP version (or ...

When iterating through a table, an error occurs stating that the property "rows" is not available on type HTMLElement (

Issue Error TS2339 - Property 'rows' does not exist on type HTMLElement when looping through table in Angular 7 Encountering error when trying to loop through HTML table in Angular 7 Currently working with Angular 7 and facing an error while ...

Struggling to reach the same level of bundle optimization with webpack + angular when compared to angular-cli

I am currently facing an issue with two Angular projects that I have. One is developed using angular-cli, while the other one is built with Webpack and utilizes @ngtools/webpack. Both projects are based on Angular 7.1.4 and @angular-devkit 0.13.5. The code ...

Tips for managing script tags within an HTML file in a Chrome extension using TypeScript and webpack

After successfully building a Chrome extension in JavaScript, the decision was made to convert it into TypeScript. This required using webpack and a tsconfig file, along with making necessary changes. The extension loaded without errors, but there are a fe ...

Using `await` inside an if block does not change the type of this expression

Within my code, I have an array containing different user names. My goal is to loop through each name, verify if the user exists in the database, and then create the user if necessary. However, my linter keeps flagging a message stating 'await' h ...

Nextjs encountered a runtime error: the function is not defined

I am looking to bring this particular function into my main tsx file: "use client" // components/LoadMore import { useState } from 'react'; export function useLoadMore() { const [postNum, setPostNum] = useState(3); const handleCli ...

Tips on how to retrieve an Observable Array instead of a subscription?

Is there a way to modify this forkJoin function so that it returns an observable array instead of a subscription? connect(): Observable<any[]> { this.userId = this.authService.userId; this.habits$ = this.habitService.fetchAllById(this.userId); this.s ...