Is there a way to dynamically extract members from an object belonging to an interface (i.e. not specifying them again explicitly), like this:
let subset = { ...someObject as ISpecific };
Currently I get all members that someObject happens to have. So the spread operator does not work here. Are there any ways to do that yet?
Example:
interface ISpecific { A: string; B: string; }
class Extended implements ISpecific { public A: string = '1'; public B: string = '2'; public C: string = '3'; }
let someObject = new Extended();
let subset = { ...someObject as ISpecific };
console.log(subset); // -> { A, B, C } but want { A, B }
TypeScript casts are merely hints for the compiler, not real conversions at runtime.
It can be achieved using decorators (see requirements at the end). It can only be used with methods (copying a property get/set accessor yields its momentary return value only, not the accessor function).
// define a decorator (@publish) for marking members of a class for export:
function publish(targetObj: object, memberKey: string, descriptor: PropertyDescriptor) {
if (!targetObj['_publishedMembers'])
targetObj['_publishedMembers'] = [];
targetObj['_publishedMembers'].push(memberKey);
}
// this function can return the set of members of an object marked with the @publish decorator:
function getPublishedMembers(fromObj: object) {
const res = {};
const members = fromObj['_publishedMembers'] || [];
members.forEach(member => { res[member] = fromObj[member].bind(fromObj); });
return res;
}
// this is for making sure all members are implemented (does not make sure about being marked though):
interface IPublishedMembers {
A(): string;
B(): number;
C(): void;
}
// this class implements the interface and has more members (that we do NOT want to expose):
class Full implements IPublishedMembers {
private b: number = 0xb;
@publish public A(): string { return 'a'; }
@publish public B(): number { return this.b; }
@publish public C(): boolean { return true; }
public D(): boolean { return !this.C(); }
public E(): void { }
}
const full = new Full();
console.log(full); // -> all members would be exposed { A(), B(), b, C(), D(), E() }
const published = getPublishedMembers(full) as IPublishedMembers;
console.log(published); // -> only sanctioned members { A(), B(), C() }
console.log(published.B()); // -> 11 = 0xb (access to field of original object works)
(This requires the compilerOption "experimentalDecorators":true in your tsconfig.json and an ES5 target, more info at http://www.typescriptlang.org/docs/handbook/decorators.html)