简体   繁体   English

Typescript class 实现接口简化

[英]Typescript class implements interface simplify

I'm creating my custom class AxiosError, but I thought the code seems to be redundant.我正在创建我的自定义 class AxiosError,但我认为代码似乎是多余的。

  //types.ts

  export interface IAxiosRequest{}
  export interface IAxiosResponse{}

  export interface IAxiosError{
    message:string
    request:IAxiosRequest
    response:IAxiosResponse
  }

  // error.ts

  import {AxiosRequestConfig, AxiosResponse, IAxiosError} from 'path/to/type.ts'

  export class AxiosError extends Error implements IAxiosError {
      isAxiosError = true
      request: AxiosRequest
      response: AxiosResponse
      constructor(params:IAxiosError){
         super(params.message)
         const {request,response} = params
         this.request = request
         this.response = response
         Object.setPrototypeOf(this, AxiosError.prototype)
      }
  }

  export function createdError(params:IAxiosError){
     return new AxiosError(params)
  }

I thought if one class implements some interface, there is no need to illustrate all the properties defined in the interface.我想如果一个 class 实现了某个接口,则无需说明接口中定义的所有属性。 I mean, if the interface has many properties, it is annoying 'cause you have to illustrate all the properties in the class again我的意思是,如果接口有很多属性,那很烦人,因为你必须再次说明 class 中的所有属性

so I hope the error.ts should be所以我希望error.ts应该是

  import { IAxiosError} from 'path/to/type.ts'

  export class AxiosError extends Error implements IAxiosError {
      isAxiosError = true
      constructor(params:IAxiosError){
         super(params.message)
         const {request,response} = params
         this.request = request
         this.response = response
         Object.setPrototypeOf(this, AxiosError.prototype)
      }
  }

  export function createdError(params:IAxiosError){
     return new AxiosError(params)
  }

but I got error.但我得到了错误。 : property doesn't exist on type AxiosError : AxiosError 类型的属性不存在

PS: I don't wanna use separate function parameters, if so when I invoke a function, I have to notice the order of the parameters PS:我不想使用单独的 function 参数,如果是这样,当我调用 function 时,我必须注意参数的顺序

 export class AxiosError extends Error implements IAxiosError {
   constructor(message:string,public request:AxiosRequest,public response:AxiosResponse){
     super(message)
     Object.setPrototypeOf(this, AxiosError.prototype)
   }
 }

 export function createError(message:string,request:AxiosRequest,response:AxiosResponse){
    return new AxiosError(message,request,response)
 }


is there a better solution?有更好的解决方案吗?

This is a longstanding open issue;这是一个长期悬而未决的问题; see microsoft/TypeScript#10570 .请参阅microsoft/TypeScript#10570 Some fixes for this had been attempted before but they had some bad consequences for existing real-world code.之前已经尝试过对此进行一些修复,但它们对现有的实际代码产生了一些不良后果。

Currently, an implements clause just checks whether or not the class conforms to the interface;目前, implements子句只检查 class 是否符合接口; it doesn't contextually type the class in any way.它不会以任何方式在上下文中键入 class。 Also note that TypeScript's type system is structural and not nominal, so " implements XXX " isn't even necessary:另请注意,TypeScript 的类型系统是结构化的而不是名义上的,因此“ implements XXX ”甚至不是必需的:

interface Foo { a: string, b: number }
function acceptFoo(foo: Foo) { }

class GoodFooExplicit implements Foo { a = ""; b = 1 } // okay
acceptFoo(new GoodFooExplicit()); // okay

class GoodFooImplicit { a = ""; b = 1 }
acceptFoo(new GoodFooImplicit()); // okay

class BadFooExplicit implements Foo { a = ""; } // error
acceptFoo(new BadFooExplicit()); // error

class BadFooImplicit { a = "" }
acceptFoo(new BadFooImplicit()); // error

In the above, a GoodFooImplicit is accepted by acceptFoo() despite not being declared as implements Foo .在上面,一个GoodFooImplicitacceptFoo()接受,尽管没有被声明为implements Foo And, for that matter, a BadFooExplicit is rejected by acceptFoo() despite being declared as implements Foo .而且,就此而言,a BadFooExplicitacceptFoo()拒绝,尽管它被声明为implements Foo Currently, the only use of implements XXX is to provide an early warning if an instance of the class would not be assignable to the interface, so you can catch the error at the class declaration (as in BadFooExplicit ) instead of having to wait until the first attempt to use an instance as if it were assignable to the interface.目前, implements XXX的唯一用途是在 class 的实例无法分配给接口时提供早期警告,因此您可以在 class 声明中捕获错误(如在BadFooExplicit中),而不必等到第一次尝试使用一个实例,就好像它可以分配给接口一样。


So, what can be done?那么,可以做些什么呢? Other than going to the above linked issue and giving it a, or giving up, the best I can think of is to use a helper function to generate a parent class that already implements whatever type you're looking for.除了转到上述链接的问题并给予它或放弃之外,我能想到的最好的方法是使用帮助器 function 来生成已经实现您正在寻找的任何类型的父 class。 For example:例如:

class AxiosError extends ClassFor<IAxiosError>().mixParent(Error) {
  isAxiosError = true
  constructor(params: IAxiosError) {
    super(params, params.message);
    Object.setPrototypeOf(this, AxiosError.prototype)
  }
}

In the above, ClassFor<IAxiosError>() would be a class constructor which takes a single parameter of type IAxiosError and returns an instance of IAxiosError .在上面, ClassFor<IAxiosError>()将是一个 class 构造函数,它接受一个IAxiosError类型的参数并返回一个IAxiosError实例。 And ClassFor<IAxiosError>().mixParent(Error) is a class constructor which takes an IAxiosError as a first parameter, and then expects the constructor arguments for the Error class, and returns an instance of IAxiosError that is also an instanceof Error . And ClassFor<IAxiosError>().mixParent(Error) is a class constructor which takes an IAxiosError as a first parameter, and then expects the constructor arguments for the Error class, and returns an instance of IAxiosError that is also an instanceof Error .

Because that first argument needs to be an IAxiosError , I refactored the implementaton of your constructor so that it calls super(params, params.message) .因为第一个参数需要是IAxiosError ,所以我重构了构造函数的实现,以便它调用super(params, params.message)

And you don't need to write implements IAxiosError , because extends ClassFor<IAxiosError>().mixParent(Error) already establishes that.而且您不需要编写implements IAxiosError ,因为extends ClassFor<IAxiosError>().mixParent(Error)已经确定了这一点。

My implementation of ClassFor looks like this:我的ClassFor实现如下所示:

function ClassFor<T extends object>() {
  function classWithParent<A extends any[], P extends object>(parent: new (...a: A) => P) {
    return class extends (parent as any) {
      constructor(obj: T, ...parentArgs: any[]) {
        super(...parentArgs);
        Object.assign(this, obj);
      }
    } as new (obj: T, ...a: A) => (T & P)
  }
  return Object.assign(classWithParent(class { }), {
    mixParent<A extends any[], P extends object>(parent: new (...a: A) => P) {
      return classWithParent(parent);
    }
  })
}

That might look complicated, but the idea is that it returns a class expression and we use type assertions to silence the compiler when it can't verify what we're doing.这可能看起来很复杂,但想法是它返回一个 class 表达式,并且当编译器无法验证我们正在做什么时,我们使用类型断言来使编译器静音。 All the type assertions are confined to that function, though, so you could presumably stick ClassFor in a library somewhere and just use it.但是,所有类型断言都仅限于 function,因此您可能可以将ClassFor粘贴在某个库中的某个地方并使用它。

Anyway, you can verify that it works using the playground link at the bottom.无论如何,您可以使用底部的游乐场链接验证它是否有效。


Okay, hope that helps;好的,希望有帮助; good luck!祝你好运!

Playground link to code Playground 代码链接

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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