通过克隆由接口定义的一组字段来创建新对象

时间:2017-10-05 12:17:01

标签: typescript

我有类Person的对象和MoneyHolder接口。如何创建一个新对象Holder,克隆Moneyholder中定义的字段子集?

class Person {
    constructor(public id: number,
        public name: string,
        public money: number) { }
}

interface MoneyHolder {
    id: number;
    money: number;
}

let person = new Person(1, 'Jack', 100);
let holder = ...????????

PS:我寻求自动解决方案而无需手动枚举接口字段。

2 个答案:

答案 0 :(得分:1)

接口在编译期间被删除,因此它们不会在运行时存在,但Person类与MoneyHolder合同完全兼容:

class Person {
    constructor(public id: number,
        public name: string,
        public money: number) { }
}

interface MoneyHolder {
    id: number;
    money: number;
}

let person = new Person(1, 'Jack', 100);
let moneyHolder: MoneyHolder = person;

所以你可以简单地将person传递给需要MoneyHolder的地方,一切都很好,这要归功于结构打字。

更新:API合约

如果要表示API合约和地图字段,可以使用类:

class Person {
    constructor(public id: number,
        public name: string,
        public money: number) { }
}

interface MoneyHolder {
    id: number;
    money: number;
}

class MoneyHolder {
    public id: number;
    public money: number;

    constructor(moneyHolder: MoneyHolder) {
        this.id = moneyHolder.id;
        this.money = moneyHolder.money;
    }
}

let person = new Person(1, 'Jack', 100);
let moneyHolder = new MoneyHolder(person);

或者你可以进入动态土地,但失去所有的类型安全:

class Person {
    constructor(public id: number,
        public name: string,
        public money: number) { }
}

class MoneyHolder {
    public id: number = 0;
    public money: number = 0;

    constructor(moneyHolder: any) {
        for (let prop of Object.getOwnPropertyNames(this)) {
            this[prop] = moneyHolder[prop];
        }
    }
}

let person = new Person(1, 'Jack', 100);
let moneyHolder = new MoneyHolder(person);

答案 1 :(得分:1)

当TypeScript编译为JavaScript时,类型信息(包括接口)将被删除。因此,您无法在运行时引用MoneyHolder,以便枚举您关注的idmoney键。

可以做的是创建您关心的键数组,TypeScript可以使用该数组的类型推断 MoneyHolder类型为了你。以下是如何让TypeScript推断字符串文字数组:

function stringLiteralArray<K extends string>(...arr: K[]): K[] {
  return arr;
}
const moneyHolderKeys = stringLiteralArray('id', 'money'); 

辅助函数stringLiteralArray()为您做推理。

(除了)

如果你刚刚这样做了:

const moneyHolderKeysOops = ['id', 'money']; // string[]

它将被推断为string[],它会丢失您关心的关键信息。你可以这样做:

const moneyHolderKeysTedious = ['id', 'money'] as ('id'|'money')[];

但这很乏味且重复。这就是我们创建stringLiteralArray()辅助函数的原因。

现在您的moneyHolderKeys数组的推断类型为('id' | 'money')[]。以下是编写通用克隆函数的方法:

function clonePart<T, K extends keyof T>(keys: K[], obj: T): Pick<T, K> {
  const ret = {} as Pick<T, K>;
  keys.forEach(k => ret[k] = obj[k]);
  return ret;
}

请注意返回值Pick<T,K>,它是T的超类型,仅指定K的键。它是标准TypeScript库的一部分,其定义为:

/**
 * From T pick a set of properties K
 */
type Pick<T, K extends keyof T> = {
    [P in K]: T[P];
};

现在你可以使用它了:

let person = new Person(1, 'Jack', 100);
let holder = clonePart(moneyHolderKeys, person);

如果您检查holder,您会看到它是{id: number, money: number}类型。嘿,那是MoneyHolder!我们现在可以根据需要命名:

type MoneyHolder = typeof holder;

这样有效。它与你所要求的有点相反,但是它完成了工作,没有任何重复。你可以see it in action in the TypeScript Playground。希望有所帮助;祝你好运!