我的一些代码中有一个工厂方法,我不仅要传递一个Class类型来实例化,还要传递一组类中属性的默认值。我想得到这样的输入,编译器将告诉我是否尝试传入实例化的类类型中不存在的属性。我不知道怎么做。
这是一个简单的例子,展示了我想做的事情。
当我尝试它时,我得到一个编译器错误:“接口可能只扩展一个类或另一个接口。”对于接口ClassProps
,因为它无法从T
扩展。
class Base {
myId: number;
}
class Klass1 extends Base {
myString: string;
myNumber: number;
}
interface IBaseCtr<T> {
new (): T;
}
// Get an interface that is the properties of the class passed
interface ClassProps<T extends Base> extends T {}
// This method compiles, but doesn't check the prop names
//function factory<T extends Base>(ctrFunc: IBaseCtr<T>,
// initialData?: Object): T
function factory<T extends Base>(ctrFunc: IBaseCtr<T>,
initialData?: ClassProps<T>): T
{
let new_obj = new ctrFunc();
Object.assign(new_obj, initialData);
return new_obj;
}
let v1 = factory(Klass1, {myId: 10, myString: 'foo'});
let v2 = factory(Klass1, {badVar: 10});
任何想法如何输入initialData
,以便将badVar
标记为在第二次通话中不被允许?
答案 0 :(得分:1)
我认为这是一个可爱的问题。
这是我的方法。完全删除该界面。
function factory<T extends V, V extends Base>(ctrFunc: IBaseCtr<T>,
initialData?: V): T
{
let new_obj = new ctrFunc();
Object.assign(new_obj, initialData);
return new_obj;
}
这设置了一个层次结构,Base是V中属性的子集,它本身是T(最终类型)中属性的子集。 v1被分配没有错误。 v2的赋值语句在编译时导致错误。
您实际上可以继续删除extends Base
部分以使此功能适用于更多地方。它仍然会以同样的方式进行类似的检查。
对于giggles,一个完全独立的工厂函数签名,类型检查正确:
function factory<T extends V, V>(ctrFunc: new () => T,
initialData?: V): T
答案 1 :(得分:0)
我做的事情与你做的有点不同,但首先是代码:
interface BaseProperties {
myId: number;
}
class Base<T extends BaseProperties> {
protected myId: number;
init(props: T): void {
this.myId = props.myId;
}
}
interface Klass1Properties extends BaseProperties {
myString: string;
myNumber: number
}
class Klass1 extends Base<Klass1Properties> {
private myString: string;
private myNumber: number;
init(props: Klass1Properties): void {
super.init(props);
this.myString = props.myString;
this.myNumber = props.myNumber;
}
}
interface IBaseCtr<T> {
new (): T;
}
function factory<P extends BaseProperties, T extends Base<P>>(ctrFunc: IBaseCtr<T>, initialData?: P): T {
let new_obj = new ctrFunc();
new_obj.init(initialData);
return new_obj;
}
let v1 = factory(Klass1, {myId: 10, myString: 'foo'});
let v2 = factory(Klass1, {badVar: 10});
这符合您的请求,因为第二次调用factory
会导致编译错误。
我将班级成员更改为私有/受保护,而不是使用Object.assign
我使用init
方法分配值。
为什么?因为这是面向对象的事情。这样,您可以在使用super.init
分配值时使用类的层次结构(如果您决定不在特定情况下,则不要调用它)。
基本上你有更多的控制方式。