The function is expected to output Promise<string>, but it seems to be outputting Promise<unknown> instead, although it still compiles successfully

Recently, I encountered some strange code that was compiling fine even though it had some issues. After fixing it, I still couldn't figure out why it was originally working:

const stringOrNull: () => Promise<string | null> = () =>
  Promise.resolve(Math.random() < 0.5 ? "Hello, world." : null);

// The mysterious code block
const stringOrNullWeird: () => Promise<string> = () =>
  new Promise(resolve =>
    stringOrNull().then(result => resolve(result ? result : undefined))
  );

// This will potentially cause a crash with an UnhandledPromiseRejectionWarning
[...Array(10).keys()].forEach(async () =>
  console.log((await stringOrNullWeird()).toLocaleLowerCase())
);

To understand this oddity, I modified the function and stored the returned Promise in a temporary variable to check TypeScript's inferred type. Surprisingly, it turned out to be unknown, leading to compilation errors.

// This doesn't compile as expected
// Type '() => Promise<unknown>' is not assignable to type '() => Promise<string>'.
//   Type 'Promise<unknown>' is not assignable to type 'Promise<string>'.
//     Type 'unknown' is not assignable to type 'string'.ts(2322)
const stringOrNullUnknown: () => Promise<string> = () => {
  // const p: Promise<unknown>
  const p = new Promise(resolve =>
    stringOrNull().then(result => resolve(result ? result : undefined))
  );
  return p;
};

This discrepancy between the two forms left me puzzled about why the initial version compiled successfully, compromising type safety?

Answer №1

It's not because Promise<unknown> can be assigned to Promise<string>. TypeScript actually infers the type correctly when you use new Promise directly as new Promise<string>. But if you assign it to a variable without specifying the type, it defaults to unknown.

The reason this assignment works is due to the signature of the Promise constructor:

new <T>(executor: (resolve: (value?: T | PromiseLike<T>) => void, reject: (reason?: any) => void) => void): Promise<T>;

Notice that the value parameter for resolve is optional, allowing for undefined as a valid value. This caused some issues in the past leading to changes being reverted.

It's generally recommended to avoid using the Promise constructor:

const stringOrNullWeird: () => Promise<string> = () =>
  stringOrNull().then(result => result ? result : undefined);

A simpler and safer approach would have been an error. If your async logic becomes complex, consider using async/await:

const stringOrNullWeird: () => Promise<string> = async () => {
  const result = await stringOrNull()
  return result ? result : undefined;
}

Both these approaches provide better type safety than directly calling the Promise constructor.

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

Transmit information using the buttonRenderer feature in Ag-Grid for Angular applications

Struggling to transfer data between two unrelated components by clicking on a cell in ag-Grid and passing the data to a form component. I utilized the buttonRenderer function to extract row data from ag-Grid, but I'm unsure how to pass it to the secon ...

Refreshing an Angular page when navigating to a child route

I implemented lazy loading of modules in the following way: { path: 'main', data: {title: ' - '}, component: LandingComponent, resolve: { images: RouteResolverService }, children: [ { path: '', redirectTo: 'home&apo ...

I want to use Angular and TypeScript to play a base64 encoded MP3 file

I am attempting to play a base64 encoded mp3 file in an Angular application. I have a byteArray that I convert to base64, and it seems like the byte array is not corrupted because when I convert it and paste the base64 string on StackBlitz https://stackbli ...

When attempting an Axios request, the backend method cannot be accessed

I am facing an issue when using Axios request to access a method in the backend. I am constrained by pre-existing code in both frontend and backend that limits how much I can modify or add. Frontend: const response = await axiosConfig.put<IReserved&g ...

Issue with triggering Observable during an Angular 2 HTTP call

In my current setup using Ionic 2 with Angular 2, I have the following method implementation: private login(params: any, url: string){ var p = new Promise<JsonResult>((resolve, reject) => { let body = JSON.stringify(params); l ...

Having trouble compiling my Angular application with success

I am working with a file named mock-values.ts. In this file, I have defined the following constants: export const TIMES: Time[] = [ { i: '8:00', v: '8' }, { i: '8:30', v: '8:30' }, { i: '9:00', v: &apo ...

The error message "vimeo/player.js - Unable to access property 'nativeElement' as it is undefined" appeared

I am encountering difficulties integrating vimeo/player.js into my angular-cli application. There isn't a supporting library for Angular 4, so I'm following the steps in the README.md under "Using with a module bundler". I created a vimeo-player ...

Using the --prod flag in Ionic 3 app on Android 7 results in the Keyboard not being displayed

After running the command ionic cordova run android --device, everything functions properly. However, when attempting the same command with the --prod flag, the input click fails to display the keyboard despite implementing the (onFocus) attribute in the & ...

Should I call `complete()` on the takeUntil Subject within the ngOnDestroy method?

To prevent memory leaks caused by Observables inside Components, I always use the takeUntil() operator before subscribing. Here is an example of how I implement it in my components: private stop$ = new Subject(); ngOnInit(): void { this.http .get( ...

Issue "Value of type '{}' cannot be assigned to parameter of type 'T | (() => T)'" encountered within a React component containing a type parameter

Currently, I am attempting to achieve the following: function SomeComponent<T>({ children }: PropsType) { const [configuration, setConfiguration] = useState<T>({}) } However, I am encountering this issue: The argument of type '{}&apos ...

What is the process for dynamically loading a concrete implementation and customizing object creation through parameterization?

Concern I am currently developing a Chrome Extension that is capable of receiving commands and is built using TypeScript. My objective is to separate out concrete implementations of a class into individual files that can be loaded dynamically. https://i. ...

Is it possible to eliminate the table borders and incorporate different colors for every other row?

Eliminating the table borders and applying color to alternate rows. Check out my code snippet: https://stackblitz.com/angular/dnbermjydavk?file=app%2Ftable-overview-example.ts. ...

Tips on adding a select tag option value to an Angular 4 web service

I am currently working on a web service that allows me to post data in order to create profiles. I have encountered an issue with a drop-down list in the form where the selected option value does not get posted. This results in an 'undefined' err ...

Enhance your Angular application with lazy loading and nested children components using named outlets

Let me start by explaining that the example provided below is a simplified version of my routes that are not functioning properly. I am working on an angular project, specifically a nativescript angular project, and I suspect the issue lies within Angular ...

Having trouble getting ng-click to function properly in TypeScript

I've been struggling to execute a function within a click function on my HTML page. I have added all the TypeScript definition files from NuGet, but something seems to be going wrong as my Click Function is not functioning properly. Strangely, there a ...

Angular Pause until the variable is ready

I am in the process of developing a new web application service. The first step involves obtaining a token through the rest API. Once this token is obtained, it needs to be sent as a header to retrieve additional information. The issue I'm facing is ...

What are the steps to resolve the issue of assigning void type to type ((event: MouseEvent<HTMLDivElement, MouseEvent>) => void) | undefined in a react application?

I'm trying to update the state isOpen to true or false when clicking on a div element, but I keep getting an error with the following code: function Parent() { const [isOpen, setIsOpen] = React.useState(false); return ( <Wrapper> ...

Issue with Date generation in TypeScript class causing incorrect date output

I have a simple code snippet where I am creating a new Date object: var currentDate = new Date(); After running this code, the output value is: Sat May 11 2019 13:52:10 GMT-0400 (Eastern Daylight Time) {} ...

Uncertain about the best way to utilize an observable

I am currently developing a real-time chat application using Angular and Node.js. The backend is powered by nodejs and I am utilizing socket.io for communication. Server Side io.on('connection', (socket) => { socket.on('new-message&apo ...

Implementing TypeScript for augmented styling properties in a component - a guide

I have custom components defined as follows: import React from 'react'; import styled from '../../styled-components'; const StyledInput = styled.input` display: block; padding: 5px 10px; width: 50%; border: none; b ...