Eldoïr
Eldoïr

Reputation: 119

Getting crazy with abstract inheritance and members visibility

this is driving me crazy.

I keep getting an error like:

'Type '{ habilete: number; endurance: number; hit: (damage: number) => void; }' is missing the following properties from type 'Monster': _habilete, _endurancets-plugin(2739)'

What I have:

export abstract class Fighter {
  constructor(habilete: number, endurance: number) {
    this._habilete = habilete
    this._endurance = endurance
  }

  public get habilete(): number {
    return this._habilete
  }
  protected set habilete(value: number) {
    this._habilete = value
  }
  private _habilete: number

  public get endurance(): number {
    return this._endurance
  }
  protected set endurance(value: number) {
    this._endurance = value
  }
  private _endurance: number

  public hit(damage: number) {
    this.endurance -= damage
  }
}

Then Monster simply extends it with nothing more (for now):

export class Monster extends Fighter {}

Then I want to use it like:

<template>
  <Card v-for="(monster, i) in gameStore.monsters" :key="i">
    <MonsterSlot :monster="monster" />
  </Card>
</template>

But on that :monster="monster", it will keep telling me that error I've pasted. MonsterSlot is simply:

<template>
  <Flex v>
    <Stat name="Habileté" :value="monster.habilete" />
    <Stat name="Endurance" :value="monster.endurance" />
  </Flex>
</template>

<script setup lang="ts">
import { Monster } from '@/model/Monster'
import Flex from './Flex.vue'
import Stat from './Stat.vue'

defineProps<{
  monster: Monster
}>()
</script>

And yes, I'm using Vue.js but I do believe this is a TypeScript issue. Because, guess what... if I just put _habilete and _endurance in public on Fighter, it just works like magic...

But why? I don't want to expose my variables just to "make it work". I want them to have a public getter and protected setter. Doesn't seem complicated :(

Thanks for any tip! I must be misunderstanding something here.

Upvotes: 1

Views: 38

Answers (1)

Estus Flask
Estus Flask

Reputation: 222309

There are multiple pitfalls associated with the reactivity of classes, this makes them much less practical than plain objects.

In this case monster is not an instance of Moster but a reactive proxy, i.e. reactive(new Monster(...), which is not the same thing. The refs inside instance properties are unwrapped at runtime. At compile time only the public properties of Monster are processed, this is reflected in the error. And private fields are a known way to enforce type incompatibility in TypeScript.

If it's known that monster isn't affected by ref unwrapping and effectively has Moster type, it needs to be typed accordingly:

const monster = reactive<Monster>(new Monster(...));

Otherwise it needs to be taken into account that monster prop is not Moster:

defineProps<{
  monster: UnwrapNestedRefs<Monster>
}>()

Upvotes: 0

Related Questions