【问题标题】:Assign all keys of type to class in TypeScript在 TypeScript 中将所有类型的键分配给类
【发布时间】:2019-07-14 15:32:06
【问题描述】:

我有一个类型,我想以某种方式分配给一个类,以便该类具有与所述类型相同的属性。

abstract class Model<T> {

    protected options: T

    constructor (options?: Partial<T>) {
        this.options = { ...this.fake(), ...options }
    }

    abstract fake () : T

}

type UserOptions = {
    userName: string,
    email: string,
    roles?: int[]
}

class User extends Model<UserOption> {

    fake () : UserOption {
        return {
            userName: getRandomUsername(),
            email: getRandomEmail()
        }
    }

}

有了这个,我可以创建新模型并实例化它们,而无需填写所有字段。任何未指定的字段仅填充随机数据。但是现在我也想直接访问这些字段,例如通过(new User()).userName。现在我可以公开options 并通过.options.userName 访问这些字段,但是当我需要在很多地方访问它们时,这将变得非常冗长。

有没有一种方法可以在正确键入实例的同时将所有这些属性通用分配给实例本身?

另外,我正在考虑使用代理并在其他辅助方法中返回它们,而不是模型本身。这样,为了获得代理,仍然必须调用一个方法,但我的其余代码将始终直接返回代理。我在abstract class Model&lt;T&gt; 中对此进行了尝试,但它没有为user.proxy() 的调用者提供任何关于属性“存在”的知识(显然,因为返回类型只是this)。

proxy () : this {
    return new Proxy(this, {
        get (target, prop: keyof T, receiver) {
            return target.options[prop]
        }
    })
}

【问题讨论】:

标签: typescript


【解决方案1】:

嗯,我认为没有简单且类型安全的解决方案。您希望能够制作class Model&lt;T&gt; extends T,或等效的mixin,但that's not legal TypeScript。您可以使用正确的 type 来制作某些东西,但是如果没有 assertion 或两个,编译器将无法理解该对象属于该类型。

我要备份一秒钟。似乎对于每个 Model&lt;T&gt; 类,您真的需要一个 static fake() 函数来生成默认的 T 实例,对吧?也就是说,您实际上并不需要从fake() 的实现中访问this。因此,我建议不要使用new User().fake(),而是采用User.fake() 的形式。

如果是这样,那么您的Model 构造函数具有类似这样的接口:

interface ModelConstructor<T> {
  fake(): T; // static default instance maker
  new(options?: Partial<T>): T; // actual constructor
}

然后我们可以有一个返回这样一个构造函数的函数,给定一个适当的fake()函数:

function makeModel<T extends object>(fake: () => T): ModelConstructor<T> {
  const ret = function (this: T, options?: Partial<T>) {
    Object.assign(this, fake(), options);
  };
  ret.fake = fake;
  // this assertion or something like it is unfortunately necessary
  return ret as any;
}

请注意,我必须进行断言,因为常规可调用函数不被视为可分配给构造函数(可更新)类型。 (在某个地方有一个 GitHub 问题,确切地说明了为什么他们决定不允许这样做,但我现在找不到它。也许其他人知道它在哪里?)

然后你可以这样做:

type UserOptions = {
  userName: string,
  email: string,
  roles?: number[]; // not int, what is int?
}

// here is a fake() for Users
function getDefaultUser(): UserOptions {
  return {
    userName: "fred",
    email: "fred@example.com",
    roles: []
  }
}

// make your new constructor:
const User = makeModel(getDefaultUser);
// const User: ModelConstructor<UserOptions>;

// use your constructor:
const user = new User({ userName: "George", email: "george@example.com" });
// const User: UserOptions;

也许这会给你一些关于如何进行的想法。希望有帮助;祝你好运!

【讨论】:

  • 谢谢!生成构造函数的makeModel 函数对我来说是正确的方法。我最终让构造函数返回this 的代理,这样我仍然可以使用(受保护的)options 对象。此外,在某些情况下,我还创建了新的类,例如 class User extends makeModel(getDefaultUser),这样我就可以拥有所有可用的选项,还可以向结果类添加方法。
猜你喜欢
  • 1970-01-01
  • 2022-07-27
  • 2022-08-23
  • 2018-09-19
  • 2018-12-11
  • 1970-01-01
  • 1970-01-01
  • 2023-03-30
  • 2016-06-24
相关资源
最近更新 更多