【问题标题】:Constructor signature that returns class with static property返回具有静态属性的类的构造函数签名
【发布时间】:2019-02-21 14:34:54
【问题描述】:

作为我昨天的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&lt;T, keyof T&gt;) 的结果类型不再向其添加任何属性,因此当我创建模型时,_symbol 属性对 TypeScript 来说是“丢失”的(这意味着 new User()._symbol 不会' t 编译)。

一种解决方法是让Model 函数的返回类型是这样的:

type ModelClass<T> = Pick<T, keyof T> & { _symbol: string }

这样,new User()._symbol 编译并返回 "user"。但是我不希望 _symbol 成为实例属性,我希望它成为静态类属性。不幸的是,我找不到任何关于如何修改这种新类型ModelClass 以指定具有静态属性的类的信息。有什么办法可以在 TypeScript 中解决这个问题?

【问题讨论】:

    标签: typescript


    【解决方案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();
    

    【讨论】:

      【解决方案2】:

      有点答非所问:让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
      }
      

      【讨论】:

      • 这似乎是一个合理的想法,但由于Property 'options' of exported class expression may not be private or protected.(即使options 标记为受保护),它无法编译。此外,我需要将一些函数注释为返回继承此模型的类的对象,为此我很确定我仍然需要正确的类型注释
      • 哦,它似乎来自我 export default'ing 那个函数。
      • @Padarom:不知道为什么这是个问题。
      • 因此,如果我将 options 设为 public,它会编译。但是它不能正确推断类型,尽管这是因为我最初没有在问题中指定的东西(类型为 Pick&lt;T, keyof T&gt; 之前的原因,我编辑了问题以澄清)
      • @Padarom:这首先不起作用,因为您不能从构造函数返回与构造的类实例不同的对象。如果要返回代理,请不要返回类,而是返回构造实例但只返回代理的工厂函数。当然,您将不再能够在常规继承中使用它。
      猜你喜欢
      • 2010-09-24
      • 1970-01-01
      • 2016-12-19
      • 2016-01-28
      • 2013-11-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-07-15
      相关资源
      最近更新 更多