1

UPDATE
i found this solution

type Merge<T, U> = {
  [K in keyof T | keyof U]: K extends keyof U? U[K]: K extends keyof T? T[K]: never;
};

is there something like this in typescript?(spread operator)

type Merge<T, U> = ...[K in keyof T]: T[K], ...[K in keyof U]: U[K]

my task is to Merge two types into a new type. Keys of the second type overrides keys of the first type. for example:

interface User {
  id: string;
  name: string;
  age: number;
  role: "admin" | "moderator" | "user";
  occupation: string;
}
interface User2 {
  id: string;
  name: string;
  age: boolean;
}

type mergedType = Merge<User, User2>

after merge, interface type will be something like this->

{
  id: string;
  name: string;
  age: boolean;
  role: "admin" | "moderator" | "user";
  occupation: string;
}
namizo
  • 11
  • 1
  • 5
  • 1
    `age: boolean`?? – T.J. Crowder Feb 13 '22 at 14:19
  • just an example – namizo Feb 13 '22 at 14:20
  • @namizo - It makes a big difference, because if `age` has type `number` in one of them and type `boolean` in the other, it markedly complicates merging. – T.J. Crowder Feb 13 '22 at 14:20
  • task says: Keys of the second type overrides keys of the first type. if type of age was number in first interface and boolean in second, age: boolean should be final result – namizo Feb 13 '22 at 14:22
  • What do you want to see if the properties from the second type are optional? What should `Merge<{a: string}, {a?: number}>` be? – jcalz Feb 13 '22 at 15:11
  • See the linked question for more info, using that solution here gives [this code](https://tsplay.dev/WJ435N). There are a lot of caveats around index signatures, optional properties, unknown properties, etc. – jcalz Feb 13 '22 at 22:15

2 Answers2

2

I think you're looking for this

Here is a working example:

interface User {
  id: string;
  name: string;
  age: number;
  role: "admin" | "moderator" | "user";
  occupation: string;
}
interface User2 {
  id: string;
  name: string;
  age: boolean;
}

type Modify<T, R> = Omit<T, keyof R> & R;

var x: Modify<User, User2> = {id: '123', name: 'name', age: true, occupation: 'occ', role: 'admin'};
console.log(x);
  • `Omit` *headslap* Yes, I should have thought of that instead of a mapped conditional type. (This question is **almost** a duplicate of that one...) – T.J. Crowder Feb 13 '22 at 15:32
1

If the types were never in conflict, it would be just be User & User2, but in your example User has age: number and User2 has age: boolean, so they conflict.

You can handle that the way you've said you want to (using the second one's definition) with a mapped type using a conditional type to pick the property type from B if it's there or from A if it isn't, intersected with the second type you're merging:

type MergeTypes<A, B> = {
    [key in keyof A]: key extends keyof B ? B[key] : A[key];
} & B;

(Edit: Or the rather simpler type MergeTypes<A, B> = Omit<A, keyof B> & B; as pointed out by M. Erim Tuzcuoglu and this answer to another question. Doh!)

Usage:

type MergedType = MergeTypes<User, User2>;

Playground link

Here's how that works:

  • key in keyof A says that the mapped type will have a key for each key of the type A
  • key extends keyof B checks to see if the key is also a key of type B
    • If it is, we use B[key] as the property type; if it isn't, we use A[key]
  • The result of the above only has the properties that A has (but with B's type for them if they both have it), it won't have ones that only exist on B. So we add & B at the end to add them.
T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875