Reputation: 2969
I am writing 2 classes, Project
and User
, each of which has a find
method that needs to call an api rest call
attempt #1
class Project {
find(params) {
return request({url: "/api/Project", qs: params});
}
}
class User {
find(params) {
return request({url: "/api/User", qs: params});
}
}
now, this is obviously not very good ;) There are no checks on the parameters, no types defined, duplicate code etc etc
attempt #2
class Base {
constructor(private name:string) {
}
find(options) {
return request({url: `/api/${this.name}`, qs: params});
}
}
class Project extends Base{
constructor() { super("Project"); }
}
class User {
constructor() { super("User"); }
}
so, slightly better. less code duplication. Still no type checking. Interfaces to the rescue ;)
attempt#3
interface IParams { token: string }
class Base {
constructor(private name:string) {
}
find(params:IParams) {
return request({url: `/api/${this.name}`, qs: params});
}
}
class Project extends Base{
constructor() { super("Project"); }
}
class User extends Base {
constructor() { super("User"); }
}
this is where I started to hit some problems. The Project and User api params object both require the token
property. However, they also require userDd
and projectId
to be set
At the moment, I need to add both of those to the IParams interface, which seems wrong.
attempt#4
interface IUserParams { userid:number, token: string }
interface IProjectParams { projectid:number, token: string }
interface IProject {
find(params:IProjectParams)
}
interface IUser {
find(params:IUserParams)
}
class Base {
constructor(private name:string) {
}
find(params) { // I have no idea on how to "type" params
return request({url: `/api/${this.name}`, qs: params}); // likewise no idea on how to type the return value
}
}
class Project extends Base implements IProject {
constructor() { super("Project"); }
}
class User extends Base implements IUser {
constructor() { super("User"); }
}
However, this does not help : as the Base class defines the find
method, how can the compiler verify that for user, userid
and token
are passed - also, that no other parameter is passed, and likewise for project
This also led me onto thinking about the return value of the find
method : for projects I want an array of IPromiseModel
, and for user, IUserModel
I have tried chaning the IProject
interface to read
interface IProject {
find(params:IProjectParams):Promise<IProjectModel[]>
}
but I still can pass any property into the params
- ie I can do
Project.find({token: "1234",foobar:true})
I suspect this is because I haven't defined a type for the parameter in the Base find
I know that generics must play a part in this, but for the life of me I cannot get a definition working that matches these requirements
I am using typescript 2.2.2
Upvotes: 0
Views: 204
Reputation: 7323
With generics you can do this:
interface IParams { token: string }
interface IUserParams { userid:number, token: string }
interface IProjectParams { projectid:number, token: string }
class Base<TEntity, TParams extends IParams> {
constructor(private name:string) {
}
find(params: TParams): Promise<TEntity[]> {
return request({url: `/api/${this.name}`, qs: params});
}
}
class Project extends Base<Project, IProjectParams> {
constructor() { super("Project"); }
}
class User extends Base<User, IUserParams> {
constructor() { super("User"); }
}
new User().find({
userid: 123,
token: 'test'
});
The constraint in the Base class TParams extends IParams
here is optional, since you are not explicitly accessing the token property.
Upvotes: 2