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

The imported path is not found in Tsconfig

Hey there! I've been working on getting my project's imports to play nice with typescript import paths. Every time I encounter this error : Error [ERR_MODULE_NOT_FOUND]: Cannot find package 'app' imported from dist/index.js It seems l ...

The tRPC setData hook is limited in its ability to access all data necessary for optimistic UI updates

As I integrate mutations using tRPC and React Query to optimistically update my UI upon adding a new item, I've encountered an issue. The problem lies in the query I'm updating, which requires specific properties like auto-generated IDs or datab ...

Angular child component displaying of table data is not looping properly

Currently, I am using Angular 13 along with Bootstrap 3 to develop an application that consists of two main components: form-component (dedicated to inputting user details) and list-component (designed to showcase all data in a table format). Within the HT ...

Using the index in Vue.js to locate a method within an HTML file

Currently, I am attempting to make use of the reference "index" located within <tr v-for="(note, index) in noteList" v-bind:key="index" in order to call shareToPublic(index). The method selectedID() allows for the selection of the ...

Exporting a Typescript class from one module and importing it into another module

I am encountering issues with my source tree structure, as outlined below: /project/ |- src/ |- moduleA |- index.ts |- classA.ts (which includes a public function called doSomething()) |- moduleB |- classB.ts Th ...

What is preventing Angular from letting me pass a parameter to a function in a provider's useFactory method?

app.module.ts bootstrap: [AppComponent], declarations: [AppComponent], imports: [ CoreModule, HelloFrameworkModule, ], providers: [{ provide: Logger, useFactory: loggerProviderFunc(1), }] ...

RxJS: the art of triggering and handling errors

This is more of a syntax question rather than a bug I'm facing. The process is straightforward: Send an HTTP request that returns a boolean value If the boolean is true, proceed If the boolean is false, log a warning and stop the flow. To handle ...

How to open a print preview in a new tab using Angular 4

Currently, I am attempting to implement print functionality in Angular 4. My goal is to have the print preview automatically open in a new tab along with the print popup window. I'm struggling to find a way to pass data from the parent window to the c ...

Tips on setting a singular optional parameter value while invoking a function

Here is a sample function definition: function myFunc( id: string, optionalParamOne?: number, optionalParamTwo?: string ) { console.log(optionalParamTwo); } If I want to call this function and only provide the id and optionalParamTwo, without need ...

Enhancing class functionality with decorators in TypeScript

Over on the TypeScript's Decorator reference page, there is a code snippet showcasing how to override a constructor with a class decorator: function classDecorator<T extends {new(...args:any[]):{}}>(constructor:T) { return class extends con ...

In React TS, the function Window.webkitRequestAnimationFrame is not supported

I'm facing an issue where React TS is throwing an error for window.webkitRequestAnimationFrame and window.mozRequestAnimationFrame, assuming that I meant 'requestAnimationFrame'. Any suggestions on what to replace it with? App.tsx import Re ...

Utilize the prototype feature from a versatile source

Can a class with a generic like class Foo<A> {} access A's prototype or use a typeguard on A, or perform any kind of logic based solely on A's type - without being given the class, interface, or instance to Foo's constructor (e.g. when ...

The Battle of Identifiers: Named Functions against Anonymous Functions in TypeScript

When it comes to performance and performance alone, which option is superior? 1) function GameLoop() { // Performing complex calculations requestAnimationFrame(GameLoop); } requestAnimationFrame(GameLoop); 2) function GameLoop() { // ...

Cleaning up HTML strings in Angular may strip off attribute formatting

I've been experimenting and creating a function to dynamically generate form fields. Initially, the Angular sanitizer was removing <input> tags, so I discovered a way to work around this by bypassing the sanitation process for the HTML code stri ...

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 ...

Utilizing Express JS to make 2 separate GET requests

I am facing a strange issue with my Express API created using Typescript. The problem revolves around one specific endpoint called Offers. While performing operations like findByStatus and CRUD operations on this endpoint, I encountered unexpected behavior ...

Even with manual installation, the npm package still encounters dependency errors

Having trouble implementing the Imgur package from NPM into my Angular web app. The installation and import seemed to go smoothly, but when initializing a variable with the package, I encounter compile errors pointing to missing dependencies like 'cry ...

Passing a method from a component to a service in Angular 9

Recently, I've been working on some websocket code that involves sending a message to the server and receiving a reply. The current implementation is functional, but I'm looking to refactor it by encapsulating it within a service and then callin ...

What is the proper way to enhance properties?

In the process of developing a Vue3 app using Typescript, one of the components is designed to receive data through props. Initially, everything functioned smoothly with the basic setup: props: { when: String, data: Object }, However, I de ...

Encountered an issue in Angular 6: Unable to access property '0' of undefined

There's an error popping up in the console saying, Cannot read property '0' of undefined, but surprisingly I'm still getting the expected result. Below is my HTML code snippet: <div class="col-md-3"> <div class="slider-prod ...