Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

TypeScript: Using Type Parameters in Generic Constraints

Tags:

"Using Type Parameters in Generic Constraints" in the TypeScript site shows the example code below. But the following error occurred:

'Type 'U[keyof U]' is not assignable to type 'T[keyof U]'. Type 'U' is not assignable to type 'T'.'

function copyFields<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 };
copyFields(x, { b: 10, d: 20 });

In fact, this does not run in the Playground. What is wrong with the code?

like image 201
Fumio Nonaka Avatar asked Jan 05 '17 05:01

Fumio Nonaka


1 Answers

It makes sense that U is not assignable to T since the object satisfying U could have additional fields that T doesn't have:

interface Foo { foo: number; }
interface Bar extends Foo { bar: number; }
interface Bar2 extends Foo { bar: string; }

function assign<T extends U, U>(b: U): T {
    const returnVal: T = b;  // error: Type 'U' is not assignable to type 'T'.
    return returnVal;
}

const bar2: Bar2 = { foo: 7, bar: "happy" };
assign<Bar, Foo>(bar2);

So, since U isn't assignable to T, there's no way we can guarantee that a specific U[keyof U] is assignable to T[keyof U].

(I'm not 100% confident in this explanation, but it seems to make sense to me.)


However, by modifying how you type things, you can write a version of copyFields that works as intended, like this:

function copyFields<T, K extends keyof T>(target: T, source: Pick<T, K>) {
    for (let id in source) {
        target[id] = source[id];
    }
    return target;
}
like image 194
JKillian Avatar answered Sep 23 '22 11:09

JKillian