Expanding one type by utilizing it as an extension of another type

I am looking to create a custom object with predefined "base" properties, as well as additional properties. It is important for me to be able to export the type of this new object using the typeof keyword. I want to avoid having to define an interface for this object because new properties are added to it frequently, and updating both the object and the interface can become cumbersome.

  • I want a compile-time error if a property is added to Base but not to derived
  • I need to export the type of derived along with all its properties
  • No need for a separate type or interface for derived
export type Base = {
  id: string;
}

// elsewhere

export const derived = {
  id: "42",
  foo: "bar"
}

If I create the object without any specific type information, I won't receive compile-time errors if it doesn't adhere to the structure defined in Base.

One initial approach would be to simply type derived as Base:

export const derived: Base = {
  id: "42",
  foo: "bar" // error: type { ... } is not assignable to { ... }
}

export type Derived = typeof derived; // The expected type should include { id: string; foo: string }

The compiler will flag an error here because even though I've typed derived as Base, I have included properties that are not part of Base.

An alternative solution is to type derived as a combination of Base and additional unknown properties using something like Record<string, unknown>:

export const derived: Record<string, unknown> & Base = {
  id: "42",
  foo: "bar"
}

export type Derived = typeof derived; // Now: Record<string, unknown> & { id: string; }

However, when using typeof derived, the resulting type does not capture the extra properties like foo, since it interprets them as keys within an arbitrary record.

The only workaround I could think of involves creating an unused variable of type Base and assigning derived to it solely for type-checking purposes:

export const derived = {
  id: "42",
  foo: "bar"
}

export type Derived = typeof derived; // This now provides the correct type

const _: Base = derived; // This will raise an error if derived doesn't adhere to the Base structure

I am wondering if there is a way to type derived so that it can extend Base without losing type information when using typeof. Is such a type definition possible?

Answer №1

Utilize the satisfies operator for this purpose.

export type Base = {
  id: string;
}

export const derived = {
  id: "42",
  foo: "bar"
} satisfies Record<string, unknown> & Base;

export type Derived = typeof derived;

// type Derived = {
//   id: string;
//   foo: string;
// }

You can also achieve the same with:

export const derived = {
  id: "42",
  foo: "bar"
} as const satisfies Record<string, unknown> & Base;

export type Derived = typeof derived;

// type Derived = {
//   readonly id: "42";
//   readonly foo: "bar";
}

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 a "undefined response" issue within an Angular

I am encountering an issue when trying to fetch data from a REST API. Upon logging the response, I am getting an undefined value. How can I resolve this? I have confirmed that the API is sending data by checking my network tab in the developer tool. getPro ...

The installation of @types/jquery leads to an unnecessary global declaration of $

In my package.json file, I have the following dependencies defined: { "dependencies": { "@types/jquery": "^3.5.5", } } This adds type declarations through @types/jquery/misc.d.ts, including: declare const jQuery: JQue ...

Incorporating TypeScript into a project originally developed in JavaScript

I'm considering using TypeScript to write code for a JavaScript project. I've come to appreciate the benefits of TypeScript and am especially interested in using it for our AngularJS 1.5 project, which we plan to migrate soon. As I'm new to ...

What is the best way to refresh NGRX data?

There are two models in a one-to-many relationship: export interface GroupModel { id: number; name: string; userIds?: number[]; } export interface UserModel { id: number; name: string; groupId?: number; } An issue arises when updating either m ...

Is there a way to efficiently convert several strings within an object that has been retrieved from an HTTP request into another language, and subsequently save this object with the

Is there a way for me to translate some strings in an object before storing it in another http request using the Google Translate API? I am currently getting the object from one http request and saving it with a put method. How can this be achieved? servi ...

A step-by-step guide on sending a fetch request to TinyURL

I have been attempting to send a post request using fetch to tinyURL in order to shorten a URL that is generated on my website. The following code shows how I am currently writing the script, however, it seems like it's not returning the shortened URL ...

Is it possible to enhance an external class with a non-static method using prototypes?

Is it possible to use prototypes to add a function for a class instance? allowing me to access this or __proto__ keyword inside my method, like so: class PersonClass { name: string; constructor(name: string) { this.name = name; } sayHello() ...

Understanding React and TypeScript Higher Order Components: How to Resolve the Type '{}' is not assignable to Type P Error

While following along with the Learning React with TypeScript book, I encountered a problem where I hit a wall while coding. Despite surrendering and copying code directly from the book, the compiler is still unhappy As a temporary solution, I have resort ...

Developing a function that takes a parameter which can be used with or without an additional argument when invoked

In my React application, I have a method that accepts a parameter for displaying a modal. const displayModal = (p:Result) => { setConfirm(true); if(p) { //check variable for truthy setSelectedRow(p); } ...

What is the most effective way to split time into two separate parts?

Suppose a user enters the time as 12:34 and we need to split it into two different parts to save it in an array like [12, 34]. How can this be achieved using Angular? I attempted to split them but my solutions were unsuccessful! I am currently utilizing & ...

Preserve the custom hook's return value in the component's state

I am currently facing a challenge in saving a value obtained from a custom hook, which fetches data from the server, into the state of a functional component using useState. This is necessary because I anticipate changes to this value, requiring a rerender ...

Interface of TypeScript Undetermined

Currently, I am developing a Demo API Wrapper specifically for Roblox. During the development process, I have come across a certain issue that I would like to address. My aim is to send a request and then return all the data in the manner of an API wrapper ...

When trying to integrate Angular.ts with Electron, an error message occurs: "SyntaxError: Cannot use import statement

Upon installing Electron on a new Angular app, I encountered an error when running electron. The app is written in TypeScript. The error message displayed was: import { enableProdMode } from '@angular/core'; ^^^^^^ SyntaxError: Cannot use impor ...

Having trouble fixing TypeScript bugs in Visual Studio Code

I am encountering a similar issue as discussed in this solution: Unable to debug Typescript in VSCode Regrettably, the suggested solution does not seem to resolve my problem. Any assistance would be greatly appreciated. My directory structure looks like ...

Is it feasible to use a component in a recursively manner?

Following a two-hour search for a solution, I decided to reach out to experts as I suspected the answer might be simpler than expected. The project in question is an Angular7 one. In my goals component, I aim to include a "goal" with a button labeled "+". ...

Transferring an event to a component nested two levels deep

Within my Angular 2 ngrx application, I am working with a structure that involves nested elements: parentContainer.ts @Component({ template: `<parent-component (onEvent)="onEvent($event)" ></parent-component>`, }) class ParentContaine ...

A different approach to handling multiple constructors in Angular 4

Angular 4 does not support having multiple constructors, so I need to find a cleaner way to instantiate my object. This is what my model looks like: export class SrcFilter { constructor(public firstList?: Array<String>, public secondList?: Arra ...

I'm puzzled as to why my createDoorMethod is returning a string value instead of a number, even though I am passing it a number. Can someone help me

Currently enrolled in a web development course, I am diving into the world of Angular 2 and TypeScript. Despite following along with the video tutorial and using the same code, my implementation is not working as expected, leaving me puzzled. Here is the ...

Exploring the capabilities of renderOption within Material-UI's AutoComplete functionality

Hey there, I've been pondering a question lately and could really use some help. I've been trying to set up my autocomplete feature to display a label in the options while having a different value. After doing some research, I learned about usin ...

Having trouble retrieving documents from a nested collection in Firebase

I am attempting to retrieve all documents from Firebase that are based on a query. Here is my current firebase structure: https://i.stack.imgur.com/tXrX8.png Even though I have two documents inside the "ListaFavorite" collection, when I check using empty ...