Как скопировать из одного объекта в другой только нужные свойства?

У меня есть два интерфейса:

interface IPerson {
   name: string;
   age: number;
}

и

interface IEmployee {
   name: string;
   age: number;
   salary: number;
}

и нужно скопировать данные из объекта типа IEmployee в объект типа IPerson. Но если это сделать так:

const person: IPerson = { ...employee };

то в объекте person окажется и свойство salary, что меня не устраивает.

С Object.assign ровно такая же ситуация.

Как мне скопировать только те свойства, которые есть в типе объекта-приёмника?

UPDATE: Забыл упомянуть, что решения, основанные на конкретных именах свойств, типа

const { name, age } = employee;
const person = { name, age };

не устраивают, т.к. в реальном приложении в этих интерфейсах много свойств, плюс в процессе разработки их состав часто меняется. Поэтому нужно универсальное решение.


Ответы (3 шт):

Автор решения: Mr. Man

Можно просто заранее создать "пустой" экземпляр класса и вместо деструктуризации пройтись по его полям и если в объекте employee есть эти поля, то забрать их. Если 100% всегда в employee будут все поля интерфейса IPerson, то провекру в цикле можно опустить

Код:

interface IPerson {
   name: string;
   age: number;
}

interface IEmployee extends IPerson {
   salary: number;
}

const PersonTemplate: IPerson = {
  name: '',
  age: 0
}

const employee: IEmployee = {name: 'Name', age: 18, salary: 1000};
const person: IPerson = {} as IPerson;

const copyField = < T extends {} > (k: keyof T, target: T, source: T) => {
    target[k] = source[k];
}

for (const key in PersonTemplate) {
    const keyName = key as keyof IPerson;

    if (keyName in employee) copyField(keyName, person, employee);
}

console.log(person);
→ Ссылка
Автор решения: Oleg Ishchenko

Доработанная до максимальной универсальности идея от @Mr. Man: одна универсальная функция + по одной функции на каждый тип, в который нужно преобразовывать:

// universal function

function createFrom<T extends {}>(source: T, template: T) {
  const target = {} as T;
  for (const key in template) {
    const keyName = key as keyof T;
    target[keyName] = source[keyName];
  }
  return target;
}

// types

interface IPerson {
   name: string;
   age: number;
}

function createPersonFrom(source: IPerson) {
  const template: IPerson = {
    name: '',
    age: 0
  }
  return createFrom(source, template);
}

interface IEmployee {
   name: string;
   age: number;
   salary: number;
}


// work

const employee: IEmployee = { name: 'Name', age: 18, salary: 1000 };

const person = createPersonFrom(employee);
console.log(person);
→ Ссылка
Автор решения: muturgan
const employee: IEployee = ???;
const {salary, ...person} = employee;

переменная person внезапно удовлетворяет интерфейсу IPerson

→ Ссылка