What is the best way to represent the concept of "having at least one existing property and not having any additional properties" using a mapped type?

Apologies for the slightly lengthy title.

Consider the following type:

type A = {
    foo: string;
    bar: number;
    baz: boolean;
}

I want to define a new "partial" type B

type B = Partial<A>

where B must have at least one property of A and only properties from A are allowed

//valid
const b1 = {
   foo: "yeah"
}

//invalid
const b2 = {}
const b3 = {lala: "lala"}
const b4 = {foo: "foo is allowed", but_not_this: false}

Answer №1

interface Person {
  name: string;
  age: number;
  isActive: boolean;

}
type AtLeastOneKey<Obj, Keys = keyof Obj> = Keys extends keyof Obj ? Pick<Obj, Keys> : never

type NonEmptyObject<T> = Partial<T> & AtLeastOneKey<T>

// Partial<Person> & (Pick<Person, "name"> | Pick<Person, "age"> | Pick<Person, "isActive">)
type ResultantType = NonEmptyObject<Person>

//compiles
const person1: ResultantType = {
  name: "John",
  age: 30,
  isActive: true
}

//errors
const person2: ResultantType = {}
const person3: ResultantType = { hobby: "swimming" }
const person4: ResultantType = { name: "Jane", isWorking: false }

Playground

Explanation

Partial<Person> & Pick<Person, "name"> | Pick<Person, "age"> | Pick<Person, "isActive">
this is the minimum required type that should be returned.

Firstly, we ensure that the object is not empty and has at least one of three properties defined.

Refer to distributive conditional types

type AtLeastOneKey<Obj, Keys = keyof Obj> = Keys extends keyof Obj ? Pick<Obj, Keys> : never

As per the documentation, Pick<Obj, Keys> - will be applied to each key. Therefore, AtLeastOneKey returns

Pick<Person, "name"> | Pick<Person, "age"> | Pick<Person, "isActive">
.

Finally, a simple intersection is used to merge the return type of AtLeastOneKey with Partial<Person>

type NonEmptyObject<T> = Partial<T> & AtLeastOneKey<T>

For more intriguing examples, you can explore my TypeScript blog

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

When set to synchronize:true in Typeorm, any changes to an entity will result in the many-to

As a newcomer to typeorm, I've encountered a peculiar issue with the synchronize: true setting in my ormconfig.js. Whenever I make changes to an entity with a Many-to-Many relationship, any data present in the join table prior to the entity modificati ...

typescript create object with immutable property already set

Can you create an object literal in JavaScript and define its interface with read-only properties simultaneously? For instance let obj = { readonly prop1: 'hello', readonly prop2: 'world' } ...

Programmatically toggle the visibility of an ion fab button

Looking for assistance in finding a method to toggle the visibility of a particular button within the collection of buttons in an ion-fab ...

When trying to access an array within a nested reactive form group, a linting error was encountered

I'm currently working on a method to delete rows from a dynamic form, but I am struggling to target the array. The structure of my form group is as follows: this.piForm = this.fb.group({ milestoneSaveModel: this.fb.group({ milestonesToCr ...

Struggling to integrate the Animejs library into my TypeScript and webpack project

Transitioning from ES5 to TypeScript and webpack, I decided to incorporate the Three.js library seamlessly. Alongside this, I wanted to utilize the Anime.js library for its impressive timeline animation features. However, my efforts yesterday were fraught ...

Adjusting the position of Angular Mat-Badge in Chrome browser

I'm currently using the newest version of Angular and I am attempting to utilize the Angular materials mat-badge feature to show the number of touchdowns a player has thrown. However, in Chrome, the badge position seems to shift when the number is inc ...

Creating a package exclusively for types on NPM: A step-by-step guide

I'm looking to set up a package (using either a monorepo or NPM) that specifically exports types, allowing me to easily import them into my project. However, I've run into some issues with my current approach. import type { MyType } from '@a ...

Change the spread operator in JavaScript to TypeScript functions

I'm struggling to convert a piece of code from Javascript to Typescript. The main issue lies in converting the spread operator. function calculateCombinations(first, next, ...rest) { if (rest.length) { next = calculateCombinations(next, ...res ...

Using Angular/Typescript to interact with HTML5 Input type Date on Firefox (FF)

Are there any Angular/Typescript projects that are completely built without relying on third-party libraries? I am encountering problems with Firefox and IE11. It works fine on Chrome where the value can be read, but the calendar does not display when us ...

Ways to indicate in Typescript that a value, if it exists, is not undefined

Is there a way to represent the following logic in TypeScript? type LanguageName = "javascript" | "typescript" | "java" | "csharp" type LanguageToWasmMap = { [key in LanguageName]: Exclude<LanguageName, key> ...

fakeAsync failing to synchronize with async task completion

Scenario In my testing process, I am evaluating a component that utilizes an observable-based service to retrieve and display data for internationalization purposes. The i18n service is custom-made to cater to specific requirements. While the component ...

What could be causing TypeScript to forego type inference and default to 'any' in this scenario?

While refactoring parts of our React app in TypeScript, I encountered a challenge that required me to use what I consider to be a less than ideal double type argument. I'm unsure if this is a bug in TypeScript or if there is some type ambiguity causin ...

Utilize dynamic components to load varying data sets multiple times

Is there a way to dynamically load a component multiple times and pass data based on certain values so that it will display with real-time information? I came across an example at the following link: In this example, there is a messageComponent with a "m ...

How can we fetch data from the server in Vue 2.0 before the component is actually mounted?

Can anyone help me with this question noted in the title? How can I prevent a component from mounting in <router-view> until it receives data from the server, or how can I fetch the data before the component is mounted in <router-view>? Here a ...

When using @testing-library/react (rtl), the 'waitFor' function achieves success even without the need for the 'await' keyword

waitFor() is causing my test to fail while waitFor() (without await) makes it pass. The official documentation states: Async methods return a Promise, so you must always use await or .then(done) when calling them. (https://testing-library.com/docs/guide ...

Issues are arising with Angular Form where the FormControl is not being properly set up for the first field in my form

After grappling with this issue for several weeks, I am still unable to pinpoint the cause. (Angular version: 16.1.4) The form component is populated using a BehaviorSubject, and although the console prints out the correct values for both the form and dat ...

TypeScript type that accommodates all object interfaces

I have several functions that all take the same type as input but return different types of interfaces. I'd like to define a type that can encompass all these functions, but when I try to do so with: const f: (arg: number) => Object = func; I enc ...

Is there a way to retrieve the request URL within the validate function of the http strategy?

Is it possible to access the context object present in guards within the validate method of my bearer strategy, by passing it as an argument along with the token? bearer-auth.guard.ts: @Injectable() export class BearerAuthGuard extends AuthGuard('be ...

Is Typescript generating error TS2411 within its own Typescript module?

After transitioning to using @types in Typescript 2.0, I executed npm i -S typescript @types/typescript to ensure everything was set up correctly with typescript. However, whenever I run tsc on my project and exclude "node_modules", I encounter the same e ...

Guide on creating a generic type that depends on the arguments provided, specifically a union type

I am dealing with the following similar types: class ActionFoo { action: 'foo'; foo: string; } class ActionBar { action: 'bar'; bar: number; } In addition, I have some handler functions for each type of defined "action", such a ...