What is the correct way to convert a base type value to its extended type in TypeScript?

Consider the TypeScript code block below:

type URLEx = URL & {
  custom: string;
};

const url = new URL("http://localhost:3000/foo/var");

const url_x: URLEx = {
  ...url,
  custom: "hello",
};

console.log(url);
// Output properties of original URL object

console.log(url_x);
// Output properties of URL object with additional 'custom' property

Upon running the code, unexpected [Symbol(context)]: URLContext and [Symbol(query)]: URLSearchParams symbols were seen. Why did this happen? How can we transform the url object into url_x without manually specifying all properties so that the resulting output resembles the following?

console.log(url_x);
// {
//   href: 'http://localhost:3000/foo/var',
//   origin: 'http://localhost:3000',
//   protocol: 'http:',
//   username: '',
//   password: '',
//   host: 'localhost:3000',
//   hostname: 'localhost',
//   port: '3000',
//   pathname: '/foo/var',
//   search: '',
//   searchParams: URLSearchParams {},
//   hash: '',
//   custom: 'hello'
// }

Answer №1

Instances of classes cannot be spread, as classes act as prototypes and object spread only performs shallow-cloning (excluding the prototype). Instead, you can achieve this by:

type URLExtended = URL & {
  custom: string;
};

const urlObject = new URL("http://example.com");
console.log({...urlObject}); // {}

const extendedURL: URLExtended = Object.assign(urlObject, {custom: 'world'})

console.log(extendedURL.hostname); // "example.com"
console.log(extendedURL.custom); // "world"
console.log(urlObject === extendedURL) // true

Check out TS Playground. Note that caution should be exercised when using Object.assign() as it will modify the original urlObject value.

Answer №2

Enhance the capabilities of the URL class:

class URLEx extends URL {
    customProperty: string
    constructor(url: string) {
        super(url);
        this.customProperty = 'hello';
    }
}

const urlx = new URLEx(location.href);

Live example in TypeScript Playground.

Also check out:

The spread syntax can be used in three main ways:

  • In function arguments list
    (myFunction(a, ...iterableObj, b))
  • In array literals
    ([1, ...iterableObj, '4', 'five', 6])
  • In object literals ({ ...obj, key: 'value' })

Answer №3

If I were to make a recommendation, I would suggest the following composition:

type URLEx = {
  url: URL;
  custom: string;
};

const url = new URL("http://localhost:3000/foo/var");

const url_x: URLEx = {
    url,
    custom: "hello"
};

console.log(url);
console.log(url_x);

The output may vary depending on the URL implementation:

[LOG]: URL: "http://localhost:3000/foo/var"
[LOG]: {
  "url": "http://localhost:3000/foo/var",
  "custom": "hello"
}

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

How can Firebase and Ionic be used to customize the password reset template for sending verification emails and more?

I'm facing an issue with firebase's auth templates not supporting my native language. Is there a way to customize the password reset template to also handle verification and email address change emails? ...

Does the React memo function modify the component's prop type?

I've come across a strange issue where defining two components causes compilation errors when written separately but not when written in the same file. test3.tsx import React from "react"; type ValueType = number[] | string[] | number | st ...

The class function in the exported typescript logs that "this" is not defined

I am currently facing an issue with my TypeScript class where I am setting class values in a constructor and referencing them using "this" in a class method. While the .ts file compiles without any warnings, when I import the compiled .js file into another ...

An error occurred while trying to access properties of null, specifically the `_rawValidators` property

I recently upgraded an app from angular 8 to angular14 and encountered a problem with a form array. The error message I'm seeing is cfs-detail.component.html:13 ERROR TypeError: Cannot read properties of null (reading '_rawValidators'). It ...

Styling <Link> component with styled-components: A step-by-step guide

Utilizing the Link component from @material-ui/core/Link in my TypeScript code was initially successful: <Link href="#" variant="body2"> Forgot? </Link> However, I am exploring the transition to styled-components located in a separate file. ...

Exploring the functionality of this TypeScript code: What's the distinction between { [key: string]: string }[] and { prop1: string, prop2: string }[]

Below is the code I am currently working with: get tags(): { [key: string]: string }[] { let tags: { [key: string]: string }[] = []; if(this.tags) { Object.keys(this.tags).forEach(x => { tags.push({ prop1: this.tags[x], prop2: g ...

Using Typescript to replicate Object.defineProperties

Is there a way to emulate Object.defineProperties from JavaScript in Typescript? I am interested in achieving something similar using the syntax of Typescript: Object.defineProperties(someObject.prototype, { property: {get: function() { return v ...

Tips for executing forEach on a promise?

I am currently working with a function that returns a promise of an array: async function functionReturningPromiseOfUserIDs(): Promise<string[]> My question is, can I use the forEach method on the array returned by this function? async function runF ...

Adding connected types to a list using Typescript

Question regarding Typescript fundamentals. In my code, I have a list that combines two types using the & operator. Here is how it's initialized: let objects: (Object & number)[] = []; I'm unsure how to add values to this list. I attem ...

When zooming out, Leaflet displays both tile layers

I'm currently working on integrating two tile layers along with a control for toggling between them. Below is the code snippet I am using: const layer1: L.TileLayer = L.tileLayer('http://{s}.tile.opencyclemap.org/cycle/{z}/{x}/{y}.png', { ...

Guide to accessing a nested and potentially optional object property with a default value and specifying its data type

Just a simple query here... my goal is to extract data.user.roles, but there's a possibility that data may be empty. In such cases, I want an empty array as the output. Additionally, I need to specify the type of user - which in this instance is any. ...

The type 'MenuOptions[]' cannot be assigned to type 'empty[]'

Even after numerous attempts, I am still grappling with TypeScript problems. Currently, I am at a loss on how to resolve this particular issue, despite all the research I have conducted. The code snippet below is what I am working with, but I am struggling ...

What is the best way to track events in angular-meteor when a user logs in, logs out, or when there is a change in the user

I am working on meteor-angular and trying to track new user login and logout changes within a single component. I have attempted to subscribe to userData in the component's initialization, but it does not seem to detect when the user logs in or out. I ...

The best approach to effectively integrate TypeScript and Fetch is by following the recommended guidelines

Currently, I am in the process of learning React and Typescript simultaneously. On the backend side, I have a server set up with ApiPlatform. For the frontend part, my goal is to utilize fetch to either create or update a Pokemon along with its abilities. ...

Ways to effectively test public functions in Typescript when using react-testing-library

I have come across the following issue in my project setup. Whenever I extend the httpService and use 'this.instance' in any service, an error occurs. On the other hand, if I use axios.get directly without any interceptors in my service files, i ...

Send a string to directive via HTML

Trying to implement a "clipboard" directive following this example. In my case, I need to dynamically compute the string to be copied to the clipboard. The goal is to pass the output of a function that generates the string to the directive. Currently, I ...

Overlooking errors in RxJs observables when using Node JS SSE and sharing a subscription

There is a service endpoint for SSE that shares a subscription if the consumer with the same key is already subscribed. If there is an active subscription, the data is polled from another client. The issue arises when the outer subscription fails to catch ...

Managing arrays in local storage with Angular 2+

I seem to be missing a crucial element in my endeavor to save and retrieve an array in local storage within my Angular 4 application. The array is fetched from the server and stored in a variable named 'aToDo' with type 'any', like so: ...

What is the best way to create a universal limitation for a larger collection of a discriminated union?

Is it possible to enforce that when defining a generic class Foo<X>, where X represents a discriminated union type, X must be a superset of another discriminated union Y? In my specific scenario, I am utilizing a discriminated union to differentiate ...

Guide to making a TreeView in Angular 2 with Typescript

How can I implement a TreeView in Angular 2 using Typescript? I have searched on Google but have not found any working examples, etc. Could someone kindly provide me with an example to help me accomplish this task? ...