I am focused on developing a type interface that includes properties defined in another interface.
Below is the schema definition for a model object in 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,
},
};
};
The key component we are discussing here is the relationships property. Each property within it represents a has-many relationship in the data model.
I aim to create a separate container for approvers - entities that determine permissions on CRUD operations based on specific relationships in the data model.
An ideal approach would be something like this:
export interface ApproverDefinition<S extends ModelSchema> {
typeName: string,
attributes: AttributesAuthorize,
relationships: {
[name: string]: RelationshipAuthorize,
}
}
To ensure consistency, I want to enforce a rule where the names of properties in ApproverDefinition.relationships must match those in the relationships property of the related ModelSchema S.
Although the above almost works conceptually, there are two TypeScript errors regarding exporting private names and treating S as a namespace.
Despite the challenges, I prefer to keep the approver logic separate from the schema for various reasons. Extending ModelSchema directly may not be feasible in this scenario.
I can resort to runtime checks by comparing keys at runtime, but I strive to achieve this validation through TypeScript itself.
Is there a way to accomplish this task using TypeScript's type system?
EDIT: Here's an example of a model schema:
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},
},
};
Now, I aim to define:
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 */,
}
}
Notice that ProfileApprover.relationships shares properties with ProfileSchema.relationships but with different values. The goal is to define rules in TypeScript ensuring all Approver instances align with their corresponding schemas.
If necessary, I could implement runtime checks for discrepancies, but I believe TypeScript should offer a way to statically enforce these rules.