What is the correct way to construct an object in TypeScript while still taking types into account?

Hey, I'm having trouble implementing a common JavaScript pattern in TypeScript without resorting to using any to ignore the types. My goal is to write a function that constructs an object based on certain conditions and returns the correct type. Here's a simplified example:

Is it feasible to make the following function work without relying on any?

function hasAB<A extends boolean, B extends boolean>(shouldHaveA: A, shouldHaveB: B): HasAB<A, B> {
    const obj: any = {}
    if (shouldHaveA) obj.a = ""
    if (shouldHaveB) obj.b = ""
    return obj
}
type HasAB<A extends boolean, B extends boolean> = 
    (A extends true ? {a: string} : {}) &
    (B extends true ? {b: string} : {}) 

const a = hasAB(true, false)
const b = hasAB(false, true)
const ab = hasAB(true, true)

The return types for a, b, and ab are accurate, but the compiler does not perform any checks on the code. For instance, you could set obj.a twice and it would go unnoticed.

// attempt 1 and 2 fails because inference takes the union of conditionals e.g. const five = true ? 5 : 4; // five: 5 | 4 function attempt1<A extends boolean, B extends boolean>(shouldHaveA: A, shouldHaveB: B): HasAB<A, B> { return { ...(shouldHaveA ? { a: "" } : {}), ...(shouldHaveB ? { b: "" } : {}) } } function attempt2<A extends boolean, B extends boolean>(shouldHaveA: A, shouldHaveB: B): HasAB<A, B> { const obj1 = shouldHaveA ? {a: ""} : {} const obj2 = shouldHaveB ? {...obj1, b: ""} : obj1 return obj2; } // attempt 3 can't get over the hurdle of needing to assign an intial type function attempt3<A extends boolean, B extends boolean>(shouldHaveA: A, shouldHaveB: B): HasAB<A, B> { let obj = {} if (shouldHaveA) { obj = { ...obj, a: "" } } if (shouldHaveB) { obj = { ...obj, b: "" } } return obj } // attempt 4 fails too function attempt4<A extends boolean, B extends boolean>(shouldHaveA: A, shouldHaveB: B): HasAB<A, B> { if (shouldHaveA) { const a = {a: ""} } if (shouldHaveB) { const b = {...(a || {}), b: ""} } const final = b || {} return final } // attempt 5 fails because ternary always assumes a union function hasAB<A extends boolean, B extends boolean>(shouldHaveA: A, shouldHaveB: B): HasAB<A, B> { const obj = {} const withA: A extends true ? {a: string} : typeof obj = shouldHaveA ? {a: ""} : obj const withB: withA & (B extends true ? {a: string} : {}) = shouldHaveB ? {...withA, b: ""} : withA return withB }

Answer №1

Currently, TypeScript does not have the capability to achieve this task. The recommended approach is to utilize available typing features for tasks like checking spelling errors in objects, even though it may not guarantee the final shape of the outcome.

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

Using Jasmine to Jest: Mocking Nested function calls

I am currently working on testing my TypeScript functions with Jasmine: //AB.ts export async function A() { } export async function B() { A(); } My goal is to unit test function B by mocking out function A to see if it is called. Here is the code I h ...

Is there a way to determine the specific type of a property or field during runtime in TypeScript?

Is there a way to retrieve the class or class name of a property in TypeScript, specifically from a property decorator when the property does not have a set value? Let's consider an example: class Example { abc: ABC } How can I access the class or ...

You are unable to apply 'use client' on a layout element in Next.js

While attempting to retrieve the current page from the layout.txt file, I encountered errors after adding 'use client' at the top of the page: Uncaught SyntaxError: JSON.parse: unexpected character at line 1 column 1 of the JSON data parseMod ...

The comparison between "rxjs-tslint" and "rxjs-tslint-rules" npm packages

Previously, I utilized the rxjs-tslint-rules package to identify RxJS-related issues in my projects. This package was included in the devDependencies section of my projects' package.json files. Now, there is a new rxjs-tslint package that introduces ...

Error in Mongoose Schema Configuration Detected in NestJS App

I'm currently developing an e-commerce application using NestJS and MongoDB with Mongoose. I've been facing an issue while trying to implement a user's shopping cart in the application. The error message I keep encountering is as follows: ...

Execute the CountUp function when the element becomes visible

Currently, I am implementing the following library: https://github.com/inorganik/ngx-countUp Is there a way to activate the counting animation only when the section of numbers is reached? In other words, can the count be triggered (<h1 [countUp]="345 ...

An empty constant object can trigger an endless cycle of re-rendering

Consider this simplified scenario: export function myCustomHook<TData = Record<string,string>> (data?: TData) { const [output, setOutput] = useState<number>(); const customFunction(data?: TData) { //In a real scenario : p ...

Implementing the 'keepAlive' feature in Axios with NodeJS

I've scoured through numerous sources of documentation, Stack Overflow threads, and various blog posts but I'm still unable to make the 'keepAlive' functionality work. What could I be overlooking? Here's my server setup: import ex ...

Angular 2 form with ng2-bootstrap modal component reset functionality

I have implemented ng2-bs3-modal in my Angular 2 application. I am now looking for a way to clear all form fields when the close button is clicked. Can anyone suggest the best and easiest way to achieve this? html <button type="button" class="btn-u ...

TypeScript focuses on checking the type of variables rather than their instance

Is there a way to pass a type (not an instance) as a parameter, with the condition that the type must be an extension of a specific base type? For example abstract class Shape { } class Circle extends Shape { } class Rectangle extends Shape { } class ...

Issues with TypeScript: outFile in tsconfig not functioning as expected

Currently, I am utilizing Atom as my primary development environment for a project involving AngularJs 2 and typescript. To support typescript, I have integrated the atom-typescript plugin into Atom. However, I noticed that Atom is generating separate .js ...

Encountering the error message "TypeError: Unable to access properties of null (reading 'get')" while utilizing useSearchParams within a Storybook file

Looking at the Next.js code in my component, I have the following: import { useSearchParams } from 'next/navigation'; const searchParams = useSearchParams(); const currentPage = parseInt(searchParams.get('page') || '', 10) || ...

`Mapping child routes in Angular using basic components`

I am encountering an issue with Angular 8 routes. The problem lies in the child routes not functioning properly. Here are my defined routes: const routes: Routes = [ { path: 'admin', component: MainComponent, children: [ { path: &apo ...

Threading in Node.js for Optimized Performance

Having trouble making axios calls in worker threads Hello, I'm working on a Node.js application and attempting to utilize worker threads for one specific task. Within the worker thread, I need to make an axios call, but I keep encountering an error w ...

Create a pipeable stream that does not trigger any events when data is piped

I have been trying to utilize the renderToPipeableStream function from React18, and although it is functional, I am struggling with handling the pipe properly. The key section of my code involves an array of strings representing HTML. I am splitting the s ...

Group data by two fields with distinct values in MongoDB

I have developed a Typescript Node.js application and I am looking to organize documents by two fields, "one_id" and "two_id", based on a specific "one_id" value. Below is the data within my collection: { "_id":"5a8b2953007a1922f00124fd", "one_id ...

Generating and setting an object property in TypeScript at runtime

In my code, I have defined an interface as follows: export interface OurHistory { ourHistory?: object; step1?:object; step2?:object; } Within the HistoryComponent class, I am doing the following: export class HistoryComponent implements OnInit, On ...

Using the -t or --testNamePattern in Jest will execute all tests

Currently, I have set up my testing framework using jest and ts-jest based on the guidelines provided by the ts-jest documentation. When I execute the command yarn test --listTests, I can identify the specific test file I intend to run: processNewUser.ts ...

Traveling from one child route to another

I am encountering issues with route navigation between child routes, as I keep receiving "routes not found" errors. Despite trying multiple solutions like injecting and refreshing after child configuration, I still face difficulties in navigating to a spec ...

Exploring the functionality of Material components within a nested child component

I am facing an issue with my TestComponent, which uses a <mat-stepper> in its template. Due to the specific context of the stepper, I have to programmatically move to the next step instead of using the matStepperNext directive on a button. Here is a ...