Extending a Type with TypeScript Generics

Encountering difficulties compiling the code below. This snippet is part of an older project with the note - "This is a TS1.8 feature. Leave it in to ensure the environment is running the right ts".

  function assign<T extends U, U>(target: T, source: U): T {
        for (let id in source) {
            target[id] = source[id]; // error TS2322: Type 'U[Extract<keyof U, string>]' is not assignable to type 'T[Extract<keyof U, string>]'.
        } 
        return target;
    }

Trying to compile it with this command

tsc -p tsconfig.json

And using this tsconfig.json file

{
  "include": [
    "Scripts/TypeScripts/**/*"
  ],
  "compilerOptions": {
    "target": "es5",
    "module": "amd",
    "sourceMap": false,
    "watch": false
  }
}

tsc -v shows Version 3.4.5.

Testing it on the playground, I also encounter the same error, raising doubts about the validity of the code. This leads me to question why I wrote that comment and how it managed to compile for 2 years (if it did).

Hence my query: Is this valid TypeScript code? If not, was it ever?

Thank you :-)

Answer №1

It seems to me that this code may not be valid, especially if you haven't worked with TS1.8 before. The issue with T extends U is that even though all of the property keys in U must exist in T, the values at those keys could be narrower. Take a look at this example:

function badAssign<T extends U, U>(target: T, source: U): T {
  for (let id in source) {
    target[id] = source[id] as any;
  }
  return target;
}

By using this function, unexpected behavior can occur. For instance:

interface Ewe {
  w: string;
  x: number;
  y: boolean;
}
interface Tee extends Ewe {
  w: "hey";
  x: 1;
  y: true;
  z: object;
}

const t: Tee = { w: "hey", x: 1, y: true, z: {} };
const u: Ewe = { w: "you", x: 2, y: false };
const newT = badAssign(t, u); 
newT.w // This will output "hey" at compile time but "you" at runtime
newT.x // Will show 1 at compile time and 2 at runtime
newT.y // True at compile time, false at runtime

This inconsistency arises because when T extends U, it means that the type of target[id] is potentially narrower than source[id], which contradicts what the function presumes.

A better approach would be to use Pick<T, K> instead of U, specifying a subset of keys from T. This ensures that every key found in target also exists in

source</code, maintaining proper value assignment:</p>

<pre><code>function assign<T, K extends keyof T>(target: T, source: Pick<T, K>): T {
  for (let id in source) {
    target[id] = source[id]; 
  }
  return target;
}

This updated method eliminates errors like the one seen previously:

assign(t, u); // Now produces an error since string is not assignable to "hey"

But allows for correct usage of assign() as intended:

let target = { a: "hey", b: 123, c: true };
let source = { a: "you", c: false };
const ret = assign(target, source);

I hope this explanation helps you improve your code structure. Good luck!

Link to code

Answer №2

It appears that you included it as a safeguard to catch errors in case TypeScript is older than version 1.8.

According to the "What's new in TypeScript" documentation for version 1.8, in the section "Type parameters as constraints":

In TypeScript 1.8, type parameters can now be used as constraints that refer to other type parameters within the same list. Previously, this would result in an error. This feature is commonly known as F-Bounded Polymorphism.

Example
function assign<T extends U, U>(target: T, source: U): T {
    for (let id in source) {
        target[id] = source[id];
    }
    return target;
}

let x = { a: 1, b: 2, c: 3, d: 4 };
assign(x, { b: 10, d: 20 });
assign(x, { e: 0 });  // Error

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 object may be null

Is there a way to verify if an object is null? let hostels = null; if (hostels[0] !== null && hostels[0].name !== null) { } However, I encountered the following error: error TS2531: Object is possibly 'null'. ...

What is the reason for Typescript not throwing an error when arguments are missing?

Here is my code snippet: type Callback = (a: string, b: string) => void const observer: Callback = function(a: string): void { console.log(a) } observer('foo') I am encountering an issue with Typescript on the last line: Expected 2 argu ...

Using Ionic with React to smoothly scroll to a selected item in a list

Ionic Scroll To Specific List Item covers Ionic + Angular. However, the solution provided is tailored to AngularJS. Now, I have a similar question but related to Ionic + React: Assuming a layout like this: <IonContent> <IonList> <Io ...

Steps for importing a CommonJS module that exports as a callable into TypeScript

I am dealing with a project that has a mixture of JavaScript and TypeScript files. Within the project, there is a JS library that follows this structure: module.exports = () => { // logic dependent on environment variables // then... return { ...

How to apply a single pipe to filter columns in Angular 2 with an array of values

I need to sort through an array of objects using multiple array string values. Here is an example of how my array of objects looks like: [{ "name": "FULLY MAINTAINED MARUTI SUZUKI SWIFT VDI 2008", "model": "Swift" }, { "name": "maruti suzuki ...

Typescript restricts dynamic property access within objects

I'm encountering some difficulties while attempting to access my object property in TypeScript: const sum = (type: string) => { return { status: 'Sum', value: data?[type].sum, }; }; sum('first') Here is a glimps ...

Several arrays within the filteredData (MatTableDataSource) are being utilized

Hey there, I could really use some assistance. I have this data stored in a filteredData variable within a MatTableDataSource. filteredData My goal is to display this data in two separate tables, but I'm encountering issues where nothing is being sh ...

Learn how to define an object with string keys and MUI SX prop types as values when typing in programming

I want to create a comprehensive collection of all MUI(v5) sx properties outside of the component. Here is an example: const styles = { // The way to declare this variable? sectionOne: { // What type should be assigned here for SXProps<Theme>? } ...

Storing the selected value from dynamically generated options in Angular using ngFor

I have a collection of items called Fixtures. Each fixture contains a group of items named FixtureParticipants. Here is my process for generating choices: <tr *ngFor="let fixture of fixtures$ | async; let i=index"> <th scope="row& ...

Can the ngx-chips library be used to alter the language of chips?

Currently, I am working with the ngx-chips library and encountering a particular issue. Here is an image representation of the problem: https://i.sstatic.net/GL3Fd.png The challenge I am facing involves updating the language of the chips based on the sele ...

Achieving CommonJS imports compilation with Typescript

In my TS file, I've included a 3rd party package using import XXX { YYY, ABC, 123 } from 'XXX'; While it compiles to CommonJS without any issues, I'd prefer to have it compiled to an ESModule instead. I tried changing the target and mo ...

Conserving node.js native imports for Electron with rollup

I am working on a project using Electron, Svelte, and Typescript. Initially, I used a specific template from here, but it restricted access to node.js built-in imports like fs for security reasons in the browser/electron frontend. However, I do not requir ...

Utilize the array map function in a React Native functional component with useState to dynamically render content

I have successfully implemented a functional component that renders a basic form with input elements. My goal is to allow users to dynamically add input elements by clicking a button. To achieve this, I am utilizing the useState hook and have created an o ...

Setting Up Absolute Imports in Node.js using TypeScript

I recently completed a project in Node using TypeScript, and I am currently experimenting with implementing absolute paths for imports. However, upon running the project, it starts to fail with the following error message: [1] Error: Cannot find module &a ...

Can TypeScript Partials be utilized for testing AWS Lambda functions?

Similar to a discussion on unit testing with typescript, I am puzzled by the incompatibility of the Partial type with the full version. In my unit test scenario, I need to check if a lambda returns 400 when the body in an AWS lambda event is not valid. To ...

Is it possible to perform remote file upload using Selenium in TypeScript?

Is there a specific way to manage remote file uploads using selenium-webdriver in typescript? Here is code that functions in javascript for this purpose: import remote from 'selenium-webdriver/remote'; // import * as remote from 'selenium- ...

Tips for creating an HTTP only cookie in NestJS

Currently, I am in the process of incorporating JWT authorization with both an accessToken and refreshToken. The requirement is to store these tokens in HTTP-only cookies. Despite attempting this code snippet, I have encountered an issue where the cookies ...

Tips for transmitting data from Dart to Typescript Cloud functions, encountering the UNAUTHENTICATED error code

Snippet of Dart code with token passed to sendToDevice: Future<void> _sendNotification() async { CloudFunctions functions = CloudFunctions.instance; HttpsCallable callable = functions.getHttpsCallable(functionName: "sendToDevice"); callable.c ...

A child support route isn't being displayed

I struggle to grasp the precise algorithm utilized by the angular router, especially when it comes to auxiliary routes. My Attempts Despite scouring through documentation, I couldn't find clear insights into how route matching works, particularly for ...

Inheriting static attributes in Typescript without using the static keyword

My project involves utilizing multiple classes that represent entities from a database. abstract class Entity { static columns: Column[]; static showInNav: boolean; static dependencies: string[]; // non-static fields } class Entity_A exten ...