我的目标是创建一个类型接口,该接口具有名称在不同接口中定义的属性。
我对typescript中的模型架构对象有以下定义。
export interface ModelSchema {
idAttribute: string;
name: string;
attributes: {
[attrName: string]:
{ type: 'number', default?: number, readOnly?: boolean} |
{ type: 'string', default?: string, readOnly?: boolean} |
{ type: 'boolean', default?: boolean, readOnly?: boolean} |
{ type: 'date', default?: Date, readOnly?: boolean} |
{ type: 'array', default?: string[] | number[], readOnly?: boolean } |
{ type: 'object', default?: object, readOnly?: boolean}
};
relationships: {
[relName: string]: {
type: RelationshipSchema,
readOnly?: boolean,
},
};
};
我们在这里讨论的关键是关系属性。它可以包含任意数量的属性,每个属性都有一个字符串名称(' children',' reply'等),每个属性代表数据模型中的has-many关系。
我想为审批者定义一个单独的容器 - 服务器可以询问的内容"该用户是否允许C / R / U / D该项/关系?"结构是平行的,因为每个关系都有不同的规则,每个关系都有自己的批准方法。
所以,理想情况下,我会说
之类的话export interface ApproverDefinition<S extends ModelSchema> {
typeName: string,
attributes: AttributesAuthorize,
relationships: {
[name: string]: RelationshipAuthorize,
}
}
然后在那里有一个单独的警卫,它基本上说:&#34; ApproverDefinition.relationships的字符串索引属性需要与S的关系属性具有相同的名称,ModelSchema通常与此相关到。
像(伪代码):
export interface ApproverDefinition<S extends ModelSchema> {
typeName: string,
attributes: AttributesAuthorize,
relationships: {
[name in keyof S.relationships]: RelationshipAuthorize,
}
}
以上几乎可以工作(除了它不需要完全覆盖,但我会接受它) - 我觉得它解释了我想要做什么,但我从TS得到两个错误。首先,我在界面上导出S私有名称,其次是我将S视为命名空间。
我需要/希望保持批准者代码与架构代码不同的原因有很多,所以在这里扩展ModelSchema并不是真的可行。
我可以进行运行时类型检查,如果我基本上检查以确保Object.keys(approver.relationships)和Object.keys(model.relationships)具有相同的条目,但我有点想要捕获它打字稿语言。
是否可以做这样的事情?
编辑:这是一个示例模型架构:
const ProfileSchema = {
idAttribute: 'id',
name: 'profiles',
attributes: {
id: { type: 'number', readOnly: true },
short_text: { type: 'string', readOnly: false },
long_text: { type: 'string', readOnly: true },
// SNIP ...
},
relationships: {
memberships: { type: Memberships, readOnly: false },
conversations: { type: ProfilePermissions, readOnly: false },
followingProfiles: { type: FollowingProfiles, readOnly: false},
followingDocuments: { type: FollowingDocuments, readOnly: false},
followingCommunities: { type: FollowingCommunities, readOnly: false},
followers: { type: FollowingProfiles, readOnly: false},
},
};
我想定义
const ProfileApprover: ApproverDefinition<ProfileSchema> = {
typeName: 'profiles'
attributes: /* attribute approver */
relationships: {
memberships: /* approver function */,
conversations: /* approver function */,
followingProfiles: /* approver function */,
followingDocuments: /* approver function */,
followingCommunities: /* approver function */,
followers: /* approver function */,
}
}
请注意,ProfileApprover.relationships与ProfileSchema.relationships具有相同的属性,但属性具有不同的值。这就是我要在类型规则中指定的 - 所有Approver实例都与其对应的模式匹配。
如果他们不在那里注册批准者,我总是会抛出运行时错误,但这感觉就像我应该能够静态地定义打字稿一样。
答案 0 :(得分:1)
如果我理解正确,你想要的是这样的
首先,我们将ModelShema的relationships属性的类型提取到一个接口中,以便我们可以独立引用它
library(splitstackshape)
dcast(cSplit(df, "sick", " ", "long")[, sick:= factor(sick, levels =
c("diarrhoea", "cough", "malaria"))], x~sick, value.var = "sick", drop = FALSE)[,
sick := df$sick][]
我们在ModelSchema中使用此接口代替以前的对象文字类型
export interface Relationships {
[relName: string]: {
type: RelationshipSchema,
readOnly?: boolean,
},
}
由于ApproverDefinition只引用ModelSchema的relationship属性,我们可以使用它现在可用的类型作为约束。这使我们可以访问keyof
使用的密钥export interface ModelSchema {
idAttribute: string;
name: string;
attributes: {
[attrName: string]:
{type: 'number', default?: number, readOnly?: boolean} |
{type: 'string', default?: string, readOnly?: boolean} |
{type: 'boolean', default?: boolean, readOnly?: boolean} |
{type: 'date', default?: Date, readOnly?: boolean} |
{type: 'array', default?: string[] | number[], readOnly?: boolean} |
{type: 'object', default?: object, readOnly?: boolean}
};
retationships: Relationships;
}
最后,由于ProfileSchema是一个值而不是一个类型,我们需要在其relationships属性上使用typeof并将结果用作ApproverDefinition的类型参数
export interface ApproverDefinition<R extends Relationships> {
typeName: string;
attributes: AttributesAuthorize;
relationships: {
[name in keyof R]: RelationshipAuthorize
}
}
const ProfileSchema = {
idAttribute: 'id',
name: 'profiles',
attributes: {
id: {type: 'number', readOnly: true},
short_text: {type: 'string', readOnly: false},
long_text: {type: 'string', readOnly: true},
// SNIP ...
},
relationships: {
memberships: {type: Memberships, readOnly: false},
conversations: {type: ProfilePermissions, readOnly: false},
followingProfiles: {type: FollowingProfiles, readOnly: false},
followingDocuments: {type: FollowingDocuments, readOnly: false},
followingCommunities: {type: FollowingCommunities, readOnly: false},
followers: {type: FollowingProfiles, readOnly: false},
},
};