构造函数签名,返回具有静态属性的类

时间:2019-02-21 14:34:54

标签: typescript

作为昨天question的后续工作:我现在有一个函数,该函数根据其参数返回一个类。

function Model<T>(name: string, defaults: () => T) : new(options: Partial<T>) => Pick<T, keyof T> {
    return class {
        public _symbol = name

        protected options: T

        constructor (options: Partial<T>) {
            this.options = { ...defaults(), ...options }
            return new Proxy(this, {
                get (target, prop: keyof T) {
                    return target.options[prop]
                }
            })
        }
    } as any
}

type Options {
    username: string,
    email: string
}

class User extends Model<Options>('user', () => ({
    username: getRandomUsername(),
    email: getRandomEmail()
}) {

    sayHello () : void {
        console.log('Hello ' + this.options.username)
    }        

}

Model函数接受一个将在每个实例中设置的名称参数,以及一个生成默认值的函数。因此,我创建的每个new User()都会有一个随机的用户名和电子邮件,但是当我使用new User({ username: 'John' })时,我仍然可以覆盖其中任何一个。

该构造函数的结果类型(Pick<T, keyof T>不会再添加任何属性,因此在创建模型时_symbol属性会丢失到TypeScript中(这意味着{ {1}}无法编译。

一种解决方法是使new User()._symbol函数的返回类型如下:

Model

这样,type ModelClass<T> = Pick<T, keyof T> & { _symbol: string } 会编译并返回new User()._symbol。但是我不希望"user"是一个实例属性,我希望它是一个静态类属性。不幸的是,我找不到有关如何修改此新类型_symbol来指定具有静态属性的类的任何信息。有没有办法解决TypeScript中的问题?

2 个答案:

答案 0 :(得分:1)

如前所述,由于返回了Proxy,因此无法正常工作。除非您需要返回返回值/默认值以外的其他任何用途的代理,否则可以通过将所有属性直接放在某个对象实例上并返回它来摆脱它。

以下是将类转换为工厂函数的示例:

export function createModel<T, E extends T>(
    name: string,
    defaults: () => E
)
{
    const factory = (options: Partial<T>) =>
    {
        return { ...defaults(), ...options };
    }

    factory._symbol = name;

    return factory;
}
type UserOptions = {
    username: string,
    email: string
}
type User = UserOptions & {
    sayHello(): void;
}

const userFactory = createModel<UserOptions, User>(
    'user',
    () => ({
        username: 'a',
        email: 'b',
        sayHello(): void
        {
            console.log('Hello ' + this.username)
        }
    })
);

const steve = userFactory({ username: 'steve' });
console.log(steve.username);
steve.sayHello();

答案 1 :(得分:0)

有点不回答:只是让TS自动解析类型。

function Model<T>(name: string, defaults: () => T) // Drop return type annotation
{
    return class
    {
        // Change this to public static
        public static _symbol = name

        protected options: T

        constructor(options: Partial<T>)
        {
            this.options = { ...defaults(), ...options }
        }
    } // Drop as cast
}