从已知对象中提取通用数值

huangapple go评论66阅读模式
英文:

Extract generic values from a known object

问题

我正在尝试构建一个接受两个对象的函数,一个对象我知道其类型,另一个对象将是通用的。是否可能从已知对象中提取通用属性?

我在这方面遇到了一些困难,但以下是我尝试过的内容:

interface IPerson {
  name: string;
  age: number;
  height: number;
  weight: number;
  job: string;
}

interface IAgeAndJob {
  age: number;
  job: string;
}

interface IHeightAndWeight {
  height: number;
  weight: string;
}

const person: IPerson = {
  name: 'Leon Kennedy',
  age: 21,
  height: 5.11,
  weight: 165,
  job: 'Police Officer'
};

const mapProperties = <IPerson, T>(person: IPerson): T => {

  let obj = {
    // 从 person 中提取存在的属性
  } as T;
  console.log(obj);

  return obj;
};

const result = mapProperties<IPerson, IAgeAndJob>(person);

如果我使用已知类型来执行此操作,我只需使用 Object.keys 获取键并遍历 person 对象以获取所需内容。是否有一种方法可以做到这一点?

英文:

I'm attempting to build a function that accepts two objects, one object I know the type of, the other will be generic. Is it possible to extract the generic properties from the known object?

I've kind of hit a wall on this one, but here's what I've tried:

interface IPerson {
  name: string;
  age: number;
  height: number;
  weight: number;
  job: string;
}

interface IAgeAndJob {
  age: number;
  job: string;
}

interface IHeightAndWeight {
  height: number;
  weight: string;
}

const person: IPerson = {
  name: &#39;Leon Kennedy&#39;,
  age: 21,
  height: 5.11,
  weight: 165,
  job: &#39;Police Officer&#39;
};

const mapProperties = &lt;IPerson, T&gt;(person: IPerson): T =&gt; {

  let obj = {
    // extract properties from person where they exist
  } as T;
  console.log(obj);

  return obj;
};

const result = mapProperties&lt;IPerson, IAgeAndJob&gt;(person);

If I was doing this with a known type I would just grab the keys using Object.keys and iterate over the person object and grab what I need. Is there a way of doing this?

答案1

得分: 2

你显示的方式无法精确执行此操作。为了理解原因,让我们看看如果没有类型信息会怎样:

const mapProperties = (person) => {
  let obj = {
    // 从 person 中提取属性(如果存在)
  };
  console.log(obj);

  return obj;
};

问题在于:在运行时,没有有关要提取哪些属性的信息,因此无法编写运行时代码来提取它们。

最接近的方法是创建一个函数,在这个函数中告诉它要提取哪些属性:

const mapProperties = <ObjectType extends IPerson, Keys extends keyof IPerson>(
    person: ObjectType,
    keys: Keys[]
): Pick<ObjectType, Keys> => {
    const obj: Partial<ObjectType> = {};
    for (const key of keys) {
        obj[key] = person[key];
    }
    return obj as Pick<ObjectType, Keys>;
};

const result = mapProperties(person, ["age", "job"]);
console.log(result);

Playground链接

我保留了对象类型的泛型,因为你使用了一个泛型类型参数,但如果这仅适用于 IPerson,你可以删除 ObjectType,直接使用 IPerson

const mapProperties = <Keys extends keyof IPerson>(
    person: IPerson,
    keys: Keys[]
): Pick<IPerson, Keys> => {
    const obj: Partial<IPerson> = {};
    for (const key of keys) {
        obj[key] = person[key];
    }
    return obj as Pick<IPerson, Keys>;
};

Playground链接

这引发了一个问题:我们从哪里获取 [ "age", "job" ] 数组?

你无法从 IAgeAndJob 获取它,但你可以反过来,从一个共同的源获取 IAgeAndJob 和该数组,这个源就是一个模型对象:

const ageAndJobModel = {
    age: 42,
    job: "programmer",
};

type IAgeAndJob = typeof ageAndJobModel;
const ageAndJobKeys = Object.keys(ageAndJobModel) as (keyof IAgeAndJob)[];

// 在其他地方使用它
const result = mapProperties(person, ageAndJobKeys);
console.log(result);

Playground链接

英文:

You can't do it in exactly the way you've shown. To see why, let's look at what it would be without any types:

const mapProperties = (person) =&gt; {
  let obj = {
    // extract properties from person where they exist
  };
  console.log(obj);

  return obj;
};

You see the problem: There's no runtime information about what properties you want to extract, so you can't write runtime code to extract them.

The closest you can come is a function where you tell it what properties you want:

const mapProperties = &lt;ObjectType extends IPerson, Keys extends keyof IPerson&gt;(
    person: ObjectType,
    keys: Keys[]
): Pick&lt;ObjectType, Keys&gt; =&gt; {
    const obj: Partial&lt;ObjectType&gt; = {};
    for (const key of keys) {
        obj[key] = person[key];
    }
    return obj as Pick&lt;ObjectType, Keys&gt;;
};

const result = mapProperties(person, [&quot;age&quot;, &quot;job&quot;]);
console.log(result);

Playground link

I kept the object type generic because you'd used a generic type parameter, but if this is only for IPerson, you can remove ObjectType and just use IPerson directly:

const mapProperties = &lt;Keys extends keyof IPerson&gt;(
    person: IPerson,
    keys: Keys[]
): Pick&lt;IPerson, Keys&gt; =&gt; {
    const obj: Partial&lt;IPerson&gt; = {};
    for (const key of keys) {
        obj[key] = person[key];
    }
    return obj as Pick&lt;IPerson, Keys&gt;;
};

Playground link

This begs the question: Where do we get the [&quot;age&quot;, &quot;job&quot;] array from?

You can't get it from IAgeAndJob, but you can flip that on its head and get IAgeAndJob and the array from a common source&nbsp;&mdash; a model object:

const ageAndJobModel = {
    age: 42,
    job: &quot;programmer&quot;,
};

type IAgeAndJob = typeof ageAndJobModel;
const ageAndJobKeys = Object.keys(ageAndJobModel) as (keyof IAgeAndJob)[];

// Using it elsewhere
const result = mapProperties(person, ageAndJobKeys);
console.log(result);

Playground link

huangapple
  • 本文由 发表于 2023年3月9日 20:18:12
  • 转载请务必保留本文链接:https://go.coder-hub.com/75684510.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定