注意:这个问题扩展了Property decorator only for a specific property type
--
我的目标是设计一个简单的ORM,其中模型可以这样定义:
@model.model()
class User extends model.Model {
@model.attr(model.StringType)
user: string;
@model.attr(model.StringType)
age: number;
}
在这个模型中,我希望编译器抛出一个类型错误,因为StringType
不能应用于number
类型的属性。但是我不能让它正常工作 - 不会抛出任何错误。
我的代码到目前为止:
interface Constructor<T> {
new (...args: any[]): T;
}
abstract class BaseType<T> {}
class StringType extends BaseType<string> {}
type RecordKey = string | symbol;
export function attr<T>(type: Constructor<BaseType<T>>) {
return <K extends RecordKey, C extends Record<K, T>>(ctor: C, key: K) => {
console.log(ctor, key);
}
}
关于这段代码如何工作的一些指示在这里非常有用。
打字稿是结构上键入的,这是整个答案的关键。在这种情况下,它意味着任何普通对象都可以是BaseType<T>
,因为它没有字段/方法。请注意,推断类型在操场上的{}
中为T
报告@model.attr(model.StringType)
。
以下任务完全没问题(number
可以是任何东西):
abstract class BaseType<T> { }
const t: BaseType<number> = { };
要求指定泛型,请尝试:
function attr<T = never>(type: Constructor<BaseType<T>>) { ... }
// ...
@model.attr<string>(model.StringType)
user: string
但请注意,它将允许@model.attr<number>(model.StringType)
,因为再次,任何对象是BaseType<number>
。
如果你的BaseType
接口尚未完成,请继续填写它,你可能会发现有足够的打字稿来锁定和推断。例如(完全随意,但你得到了想法,可以测试推理):
abstract class BaseType<T> {
deserialize: (s: string) => T;
}