简体   繁体   English

出现错误:类型“typeof B”不可分配给扩展 A 的 class B 的类型“typeof A”

[英]Getting error: Type 'typeof B' is not assignable to type 'typeof A' for class B that extends A

Reproducible example here此处可重现的示例

My need is: the contentType parameter should accept any class object extended from Content (PublicContent, AdminContent, PrivateContent, etc) and I want to call a static method from this parameter type inside the execute method.我的需要是: contentType参数应该接受从 Content(PublicContent、AdminContent、PrivateContent 等)扩展的任何 class object 并且我想在此参数类型的execute方法中调用 static 方法

I have a method with the following signature:我有一个具有以下签名的方法:

async execute<U extends ContentProps>(input: {
    contentType: typeof Content;
    contentPropsType: typeof ContentProps; 
}): Promise<Result<U, Failure>>;

and a class hierarchy as follows:和 class 层次结构如下:

// content.entity.ts

export class ContentProps extends EntityProps {}

export class Content<T extends ContentProps> extends Entity<T> {
  public constructor(props: T) {
    super(props);
  }
}

// public-content.entity.ts
export class PublicContentProps extends ContentProps {
  readonly title: string;
  readonly text: string;
}

export class PublicContent extends Content<PublicContentProps> {
  constructor(props: PublicContentProps) {
    super(props);
  }
  // ommited
}

The issue is that when I call the execute method passing PublicContent as the contentType parameter I'm getting an error saying问题是,当我调用传递PublicContent作为contentType参数的execute方法时,我收到一条错误消息

Type 'typeof PublicContent' is not assignable to type 'typeof Content'类型“typeof PublicContent”不可分配给类型“typeof Content”

The method call is:方法调用是:

const result = await this.getContent.execute({
  contentType: PublicContent,
  contentPropsType: PublicContentProps,
});

My question is: Why I'm getting this error since PublicContent is extending Content ?我的问题是:为什么我会收到此错误,因为PublicContent正在扩展Content

EDIT: as requested by @Chase, the full types for Entity and EntityProps :编辑:根据@Chase 的要求, EntityEntityProps的完整类型:

// entity.ts
export abstract class EntityProps extends BaseEntityProps {
  id?: string;
  createdAt?: Date;
  updatedAt?: Date;
}

export abstract class Entity<T extends EntityProps> extends BaseEntity<T> {
  get id(): string {
    return this.props.id;
  }

  get createdAt(): Date {
    return this.props.createdAt;
  }

  get updatedAt(): Date {
    return this.props.updatedAt;
  }

  protected constructor(entityProps: T) {
    super(entityProps);
  }
}


// base.entity.ts
export abstract class BaseEntityProps {}

export abstract class BaseEntity<T extends BaseEntityProps> extends Equatable {
  protected readonly props: T;

  protected constructor(baseEntityProps: T) {
    super();
    this.props = baseEntityProps;
  }

  static create<T = BaseEntity<BaseEntityProps>, U = BaseEntityProps>(
    this: {
      new (entityProps: U): T;
    },
    propsType: { new (): U },
    props: U,
  ): Result<T, ValidationFailure> {
    const violations = validateSchemaSync(propsType, props);

    return violations?.length
      ? Result.fail(new ValidationFailure(violations))
      : Result.ok(new this({ ...props }));
  }

  toJSON(): T {
    return this.props;
  }
}

The problem you're running into is that superclass/subclass constructors do not always form a type hierarchy even if their instances do.您遇到的问题是超类/子类构造函数并不总是形成类型层次结构,即使它们的实例确实如此。 Let's look at an example:让我们看一个例子:

class Foo {
  x = 1;
  constructor() { }
  static z = 3;
}

class Bar extends Foo {
  y: string;
  constructor(y: number) {
    super()
    this.y = y.toFixed(1);
  }
}

Here, class Bar extends Foo means if you have a value of type Bar , you can assign it to a variable of type Foo :在这里, class Bar extends Foo意味着如果你有一个Bar类型的值,你可以将它分配给一个Foo类型的变量:

const bar: Bar = new Bar(2);
const foo: Foo = bar; // okay

But if you try to assign the Bar constructor (of type typeof Bar ) to a value of the same type as the Foo constructor (of type typeof Foo ), it fails:但是,如果您尝试将Bar构造函数typeof Bar类型)分配给与Foo构造函数( typeof Foo类型)相同类型的值,则会失败:

const fooCtor: typeof Foo = Bar; // error!
// Type 'new (y: number) => Bar' is not assignable to type 'new () => Foo'

That's because the Bar constructor requires a parameter of type number when you call its construct signature (ie, new Bar(2) ), while the Foo constructor takes no parameters at all (ie, new Foo() ).这是因为Bar构造函数在调用其构造签名时需要一个number类型的参数(即new Bar(2) ),而Foo构造函数根本不接受任何参数(即new Foo() )。 If you try to use Bar as if it were the Foo constructor, and call it with no parameters, you'll get a runtime error:如果您尝试像使用Foo构造函数一样使用Bar ,并且不带参数调用它,您将收到运行时错误:

const oopsAtRuntime = new fooCtor(); // TypeError: y is undefined

For the same reason, your PublicContent constructor is not assignable to typeof Content .出于同样的原因,您的PublicContent构造函数不能分配给typeof Content The former requires a construct signature parameter of type PublicContentProps , while the latter will accept any parameter of type that extends ContentProps .前者需要一个PublicContentProps类型的构造签名参数,而后者将接受任何扩展ContentProps类型的参数。 If you try to use PublicContent as if it were the Content constructor, you might pass it a parameter of some type other than PublicContentProps , and that could lead to errors.如果您尝试将PublicContent用作Content构造函数,您可能会向它传递PublicContentProps以外的某种类型的参数,这可能会导致错误。


So let's step back.所以让我们退后一步。 In fact you don't care if the object you pass as contentType is assignable to the Content constructor type because you're not going to call its construct signature with an arbitrary ContentProps .实际上,您并不关心您作为contentType传递的 object 是否可分配给Content构造函数类型,因为您不会使用任意ContentProps调用其构造签名。 You really only care about the type of its static create() method.你真的只关心它的 static create()方法的类型。 I'd be inclined to write getContent() as a generic function like this:我倾向于将getContent()写为通用的 function,如下所示:

const getContent = <U extends ContentProps, T extends BaseEntity<U>>(input: {
  contentType: Pick<typeof Content, "create"> & (new (entityProps: U) => T);
  contentPropsType: new () => U;
}): U => { /* impl */ };

That should work similarly to your existing version on the inside of the function, and now you can call it without error, because PublicContent matches the create method of Content , as well as being a constructor of type (new (entityProps: PublicContentProps) => PublicContent) :这应该与 function 内部的现有版本类似,现在您可以毫无错误地调用它,因为PublicContent匹配Contentcreate方法,并且是类型(new (entityProps: PublicContentProps) => PublicContent)

const entity = getContent({
  contentType: PublicContent,
  contentPropsType: PublicContentProps,
}); // okay

Playground link to code Playground 代码链接

Try to make the Content implements a simple interface, like IContent , and use this interface to get the typeof on execute parameter尝试让Content实现一个简单的接口,比如IContent ,并使用这个接口来获取execute参数的typeof

暂无
暂无

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

相关问题 TYPESCRIPT :类型“typeof X”的参数不能分配给类型为“Y”的带有扩展的参数 - TYPESCRIPT : Argument of type 'typeof X' is not assignable to parameter of type 'Y' with extends EnforceAjax的类型不能分配给中间件类型 - typeof EnforceAjax is not assignable to type Middleware TypeScript TS2322:类型'typeof Foo'不能分配给'IFoo'类型 - TypeScript TS2322: Type 'typeof Foo' is not assignable to type 'IFoo' typeof不适用于未定义的类型 - typeof not working for undefined type Typescript:a 类型的参数不能分配给 b 类型的参数 - Typescript: argument of type a is not assignable to parameter of type b 当 Type B 以某种方式“扩展”Type A 时,如何停止 TypeScript 错误“Type A 没有与 Type B 相同的属性” - How to stop TypeScript error 'Type A has no properties in common with Type B' when Type B "extends" Type A in some way expressjs:打字稿:类型&#39;typeof的参数<express.Router> &#39; 不能分配给类型为 &#39;RequestHandlerParams&#39; 的参数 - expressjs: typescript: Argument of type 'typeof <express.Router>' is not assignable to parameter of type 'RequestHandlerParams' 具有Typescript和react-redux的状态组件:类型&#39;typeof MyClass&#39;的参数不能分配给类型Component的参数 - Stateful Component with Typescript and react-redux: Argument of type 'typeof MyClass' is not assignable to parameter of type Component 无法将类型&#39;typeof C:/ Users / pfftd / projectcrystal / src / actions / crystalActions&#39;的参数分配给&#39;ActionCreatorsMapObject&#39;类型的参数) - Argument of type 'typeof C:/Users/pfftd/projectcrystal/src/actions/crystalActions' is not assignable to parameter of type 'ActionCreatorsMapObject') 打字稿中的“ typeof x”是什么类型? - What is the type of `typeof x` in typescript?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM