有没有办法从属于接口的对象中动态提取成员(即不再显式指定它们),如下所示:
let subset = { ...someObject as ISpecific };
Run Code Online (Sandbox Code Playgroud)
目前我得到someObject碰巧拥有的所有成员.所以扩散运营商在这里不起作用.有没有办法做到这一点?
例:
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 }
Run Code Online (Sandbox Code Playgroud)
TypeScript强制转换只是编译器的提示,而不是运行时的实际转换.
Tit*_*mir 13
由于在运行时不存在typescript接口,因此我们不能使用它们来指导任何运行时行为,只需编译时类型检查.但是,我们可以创建一个与接口具有相同属性的对象(true例如,具有类型的所有属性以简化初始化),并且如果此对象具有接口的任何更多或更少的字段,则使编译器触发错误.我们可以使用此对象作为我们提取的属性的指南:
function extract<T>(properties: Record<keyof T, true>){
return function<TActual extends T>(value: TActual){
let result = {} as T;
for (const property of Object.keys(properties) as Array<keyof T>) {
result[property] = value[property];
}
return result;
}
}
interface ISpecific { A: string; B: string; }
const extractISpecific = extract<ISpecific>({
// This object literal is guaranteed by the compiler to have no more and no less properties then ISpecific
A: true,
B: true
})
class Extended implements ISpecific { public A: string = '1'; public B: string = '2'; public C: string = '3'; }
let someObject = new Extended();
let subset = extractISpecific(someObject);
Run Code Online (Sandbox Code Playgroud)
Fen*_*ton 11
如果你想限制你使用的类型,你可以简单而安全地做到这一点:
let subset = someObject as ISpecific;
Run Code Online (Sandbox Code Playgroud)
这些属性仍然存在,subset但编译器将阻止您依赖它们,即subset.age在下面会失败,尽管该属性仍然存在。
interface ISpecific {
name: string;
}
const someObject = {
name: 'Fenton',
age: 21
};
let subset = someObject as ISpecific;
console.log(subset.age);
Run Code Online (Sandbox Code Playgroud)
您确实可以通过像这样的解构来放弃属性,危险在于您需要在之前的列表中包含“所有我不想要的东西” ...subset。
interface ISpecific {
name: string;
}
const someObject = {
name: 'Fenton',
age: 21
};
let { age, ...subset } = someObject;
console.log(JSON.stringify(someObject));
console.log(JSON.stringify(subset));
Run Code Online (Sandbox Code Playgroud)
我遇到的另一个简单的选择是使用 lodash 的pick功能。这有点乏味,但工作做得很好。
\n首先,定义一个代表您的接口的类。稍后您将需要它来轻松创建该类的对象。
\nclass Specific {\n constructor(readonly a?: string, readonly b?: string) {}\n}\ninterface ISpecific extends Specific {}\ninterface IExtended extends ISpecific {\n c: string;\n}\nRun Code Online (Sandbox Code Playgroud)\n然后假设这是您要从中提取数据的原始对象:
\nconst extended: IExtended = { a: \'type\', b: \'script\', c: \'is cool\' };\nRun Code Online (Sandbox Code Playgroud)\n有趣的来了。Specific根据类的新实例以及pick原始对象中的成员获取键列表。
\n换句话说:
const specificMembers: string[] = Object.keys(new Specific());\nconst specific: ISpecific = lodash.pick(extended, specificMembers);\nconsole.log(specific); // {a: "type", b: "script"}\nRun Code Online (Sandbox Code Playgroud)\n瞧\xc3\xa0!:)
\n可以使用装饰器来实现(见最后的要求)。 它只能与方法一起使用(复制属性 get/set 访问器仅产生其瞬时返回值,而不是访问器函数)。
// 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)
Run Code Online (Sandbox Code Playgroud)
(这需要 tsconfig.json 中的 compilerOption "experimentalDecorators":true 和 ES5 目标,更多信息请访问http://www.typescriptlang.org/docs/handbook/decorators.html)
| 归档时间: |
|
| 查看次数: |
3665 次 |
| 最近记录: |