简体   繁体   中英

typescript classes, interfaces and generics

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

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM