2017-10-05 29 views
0

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

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:我在寻找一个无需手动枚举接口字段的自动解决方案。

回答

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和一切都很好,感谢结构打字。

UPDATE: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); 
+0

1)我需要通过HTTP发送数据来消除其他属性2)keyof是在运行时使用接口类型信息的完美示例https://www.typescriptlang.org/docs/handbook/release-notes/打字稿-2-1.html – Rem

+0

我敢肯定,问题是要求一个新的对象与原始对象的相关属性的副本。我也怀疑这个问题意味着*每个*属性(包括'name')都应该被复制,但我可能是错的。 – jcalz

+0

@Rem但'keyof'不允许你在运行时做任何事情。它只存在于设计时。如果你检查编译过的JavaScript,那里就不会有任何对应'keyof'的东西。 – jcalz

1

当TypeScript编译为JavaScript时,类型信息(包括接口)被清除。因此,您无法在运行时参考MoneyHolder,以列举您关心的idmoney键。

什么你可以要做的就是创建你所关心的按键阵列,并且打字稿可以使用数组的类型推断一个类型MoneyHolder你。以下是如何获得打字稿推断字符串常量数组:

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键的超类型。这是标准的打字稿库的一部分,它的定义是:

/** 
* 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。希望有所帮助;祝你好运!