可以实现一个具有受保护属性的 TypeScript 类吗?

huangapple go评论63阅读模式
英文:

Is it possible to implement Typescript class which has a protected property?

问题

I'm trying to mock a class of a third-party library which contains a protected property and have encountered an error message.

Here's a small reproducible example:

class WithProtected {
    protected property: number = 1
}

class Derived implements WithProtected {
    property = 3
}

This results in the following error message:

Class 'Derived' incorrectly implements class 'WithProtected'. Did you mean to extend 'WithProtected' and inherit its members as a subclass?
  Property 'property' is protected but type 'Derived' is not a class derived from 'WithProtected'.(2720)

I'm facing challenges with mocking because the third-party library is Cocos Creator and is not located in the node_modules directory. TypeScript is only working with it due to a path entry in the tsconfig.json file of the project.

Since I cannot directly import from the library, I have to write a significant amount of mocks without importing members directly from 'cc,' using import type {} from 'cc'. This prevents me from extending these classes and implementing only the mocks.

If I could find a way to implement a class that contains a private property, it would be very helpful.

英文:

I'm trying to mock a class of a third party library which contains a protected property and have got an error message.

here's a small reproducible example:

class WithProtected {
    protected property: number = 1
}

class Derived implements WithProtected {
    property = 3
}

which outputs the following error message:

Class 'Derived' incorrectly implements class 'WithProtected'. Did you mean to extend 'WithProtected' and inherit its members as a subclass?
  Property 'property' is protected but type 'Derived' is not a class derived from 'WithProtected'.(2720)

I'm kinda stuck to this mocking because the third party lib is Cocos Creator and doesn't live in the node_modules library. The only way Typescript is working with it is because it is linked via a path entry in the tsconfig.json file of the project.

So I'm not able to use the mocking out of the box from jest.

Also, as I cannot import directly from the lib itself, I have to write a fair amount of mock without importing members directly from 'cc' but with the import type {} from 'cc' which doesn't allow me to extend said classes and implement only the mocks.

Maybe too much details on this but if I could at least find a way to implement a class which contains a private prop, this would probably save me!

答案1

得分: 1

Classes with private or protected methods are intentionally incompatible with other classes or types, even if they are structurally identical. This is one of the few places in TypeScript where types are treated nominally instead of structurally, and so the problem you're facing is considered a feature. If you need compatibility with a private/protected property, you need your type to originate from the same declaration site.

This implies that you'll never get another class statement to work directly.

Instead, you could use type assertions on a class expression to claim that your new class is compatible with the other one. Maybe like this:

import type { WithProtected } from "./theLib";

const MyWithProtected = (class {
  property = 2;
} as any) as new () => WithProtected;

That compiles with no error (although the compiler is resistant and you need to use two type assertions to overcome its objections). Now you have a MyWithProtected class constructor that the compiler believes makes instances of your imported WithProtected type.

And then if you need to make subclasses via extends you can do so:

class Derived extends MyWithProtected {
  property = 3;
}

Codesandbox link to code

英文:

Classes with private or protected methods are intentionally incompatible with other classes or types, even if they are structurally identical. This is one of the few places in TypeScript where types are treated nominally instead of structurally, and so the problem you're facing is considered a feature. If you need compatibility with a private/protected property, you need your type to originate from the same declaration site.

This implies that you'll never get another class statement to work directly.


Instead, you could use type assertions on a class expression to claim that your new class is compatible with the other one. Maybe like this:

import type { WithProtected } from "./theLib";

const MyWithProtected = (class {
  property = 2;
} as any) as new () => WithProtected;

That compiles with no error (although the compiler is resistant and you need to use two type assertions to overcome its objections). Now you have a MyWithProtected class constructor that the compiler believes makes instances of your imported WithProtected type.

And then if you need to make subclasses via extends you can do so:

class Derived extends MyWithProtected {
  property = 3;
}

Codesandbox link to code

huangapple
  • 本文由 发表于 2023年5月22日 23:32:26
  • 转载请务必保留本文链接:https://go.coder-hub.com/76307784.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定