# Detecting Class+ types

**URL:** <https://forum.crystal-lang.org/t/detecting-class-types/1090>\
**Category:** Help & Support\
**Created:** [September 3, 2019, 2:31pm UTC](https://forum.crystal-lang.org/t/detecting-class-types/1090 "2019-09-03T14:31:34Z")\
**Posts on this page:** 13\
**Page:** 1

<div class="post-metadata">

**Author:** ![stakach](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/stakach/32/58_2.png) [@stakach](https://forum.crystal-lang.org/u/stakach)\
**Post date:** [September 3, 2019, 2:31pm UTC](https://forum.crystal-lang.org/t/detecting-class-types/1090/1 "2019-09-03T14:31:34Z")

</div>

Hi all,

I want to work with some instances of a class Hero that is stored in an array:

```
class Hero
  def test
    puts the_type
  end
  
  macro the_type
    {{@type.id.stringify}}
  end
end

class Super < Hero
  def test
    puts "super test"
  end
end

me = [] of Hero
me << Hero.new
me.each(&.test)

```

outputs

```
Hero+

```

However I want to reference the Hero class TypeNode as the Hero+ TypeNode gives me an error when I try to extract a constant from it.

```
BUG: Hero+ has no types

```

My actual code is slightly more complex but really just wondering if there is a way to effectively resolve down to the nearest class from a Class+ TypeNode  
Or is it possible to ensure that the array is strictly only storing Hero types and not any superclass types?

Possibly related to: [https://github.com/crystal-lang/crystal/issues/5757](https://github.com/crystal-lang/crystal/issues/5757)

---

<div class="post-metadata">

**Author:** ![Blacksmoke16](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/blacksmoke16/32/1241_2.png) [@Blacksmoke16](https://forum.crystal-lang.org/u/Blacksmoke16)\
**Post date:** [September 3, 2019, 2:44pm UTC](https://forum.crystal-lang.org/t/detecting-class-types/1090/2 "2019-09-03T14:44:05Z")

</div>

> [@stakach](#):
>
> Or is it possible to ensure that the array is strictly only storing Hero types and not any superclass types?

See [Virtual and abstract types - Crystal](https://crystal-lang.org/reference/syntax_and_semantics/virtual_and_abstract_types.html)

Could you not just make your `Hero` class abstract?

---

<div class="post-metadata">

**Author:** ![asterite](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/asterite/32/60_2.png) [@asterite](https://forum.crystal-lang.org/u/asterite)\
**Post date:** [September 3, 2019, 2:50pm UTC](https://forum.crystal-lang.org/t/detecting-class-types/1090/3 "2019-09-03T14:50:00Z")

</div>

This works:

```crystal
class Hero
  def test
    puts {{@type}}
  end
end

class Super < Hero
end

me = [] of Hero
me << Hero.new
me << Super.new
me.each(&.test)

```

When you use `@type` inside a method it automatically gets defined in each subtype separately to be able to resolve it differently.

Maybe it’s a bit confusing… something to improve.

---

<div class="post-metadata">

**Author:** ![asterite](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/asterite/32/60_2.png) [@asterite](https://forum.crystal-lang.org/u/asterite)\
**Post date:** [September 3, 2019, 3:03pm UTC](https://forum.crystal-lang.org/t/detecting-class-types/1090/4 "2019-09-03T15:03:10Z")

</div>

> [@stakach](#):
>
> Or is it possible to ensure that the array is strictly only storing Hero types and not any superclass types?

No, this is not possible.

---

<div class="post-metadata">

**Author:** ![stakach](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/stakach/32/58_2.png) [@stakach](https://forum.crystal-lang.org/u/stakach)\
**Post date:** [September 3, 2019, 9:00pm UTC](https://forum.crystal-lang.org/t/detecting-class-types/1090/5 "2019-09-03T21:00:56Z")

</div>

```
class Hero
  def test
    puts {{@type.id.stringify}}
  end
end

class Super < Hero
end

me = [] of Hero
me << Hero.new
me << Super.new
me.each(&.test)

```

outputs

```auto
Hero+
Super

```

Which I feel is unexpected as I understand that the contents of the array should be `Hero+` however I’d expect the type inside the `Hero` class itself to be `Hero`

---

<div class="post-metadata">

**Author:** ![stakach](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/stakach/32/58_2.png) [@stakach](https://forum.crystal-lang.org/u/stakach)\
**Post date:** [September 3, 2019, 9:03pm UTC](https://forum.crystal-lang.org/t/detecting-class-types/1090/6 "2019-09-03T21:03:27Z")

</div>

Which causes something like this to fail - however this works if you don’t use an array anywhere

```
class Hero
  CONST = "1234"
  
  def test
    puts {{([@type] + @type.ancestors.select { |c| c.has_constant?("CONST") }).map { |c| c.constant("CONST") } }}
  end
end

class Super < Hero
  CONST = "5678"
end

me = [] of Hero
me << Hero.new
me << Super.new
me.each(&.test)

```

outputs

```
Error: BUG: Hero+ has no types

```

whereas this

```
#me = [] of Hero
#me << Hero.new
#me << Super.new
#me.each(&.test)

Hero.new.test
Super.new.test

```

outputs

```auto
["1234"]
["5678", "1234"]

```

---

<div class="post-metadata">

**Author:** ![asterite](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/asterite/32/60_2.png) [@asterite](https://forum.crystal-lang.org/u/asterite)\
**Post date:** [September 3, 2019, 9:58pm UTC](https://forum.crystal-lang.org/t/detecting-class-types/1090/7 "2019-09-03T21:58:12Z")

</div>

> [@stakach](#):
>
> outputs
> 
> ```auto
> Hero+
> Super
> 
> ```

I see `Hero` and `Super` when I try that code locally and also in play: [Carcin](https://play.crystal-lang.org/#/r/7h82)

---

<div class="post-metadata">

**Author:** ![asterite](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/asterite/32/60_2.png) [@asterite](https://forum.crystal-lang.org/u/asterite)\
**Post date:** [September 3, 2019, 9:59pm UTC](https://forum.crystal-lang.org/t/detecting-class-types/1090/8 "2019-09-03T21:59:52Z")

</div>

Maybe also explain what you are trying to achieve? (the ultimate problem you want to solve). Maybe there’s a way to do it without macros at all.

---

<div class="post-metadata">

**Author:** ![stakach](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/stakach/32/58_2.png) [@stakach](https://forum.crystal-lang.org/u/stakach)\
**Post date:** [September 4, 2019, 1:14pm UTC](https://forum.crystal-lang.org/t/detecting-class-types/1090/9 "2019-09-04T13:14:50Z")

</div>

The code in your example is different to mine, please see: [https://play.crystal-lang.org/#/r/7hk8](https://play.crystal-lang.org/#/r/7hk8)

I want to be able to collect the values of constants in an inheritance hierarchy - it’s not even really that much of an issue in practice, I just had a spec that was using an array and it didn’t compile due to the type being `Hero+` and it seems like an issue.

I feel like these two bits of code should be equivalent

- [https://play.crystal-lang.org/#/r/7hkg](https://play.crystal-lang.org/#/r/7hkg)
- [https://play.crystal-lang.org/#/r/7hkh](https://play.crystal-lang.org/#/r/7hkh)

---

<div class="post-metadata">

**Author:** ![asterite](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/asterite/32/60_2.png) [@asterite](https://forum.crystal-lang.org/u/asterite)\
**Post date:** [September 4, 2019, 1:29pm UTC](https://forum.crystal-lang.org/t/detecting-class-types/1090/10 "2019-09-04T13:29:58Z")

</div>

Ah, yes… Please report a bug. Thank you!

---

<div class="post-metadata">

**Author:** ![asterite](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/asterite/32/60_2.png) [@asterite](https://forum.crystal-lang.org/u/asterite)\
**Post date:** [September 4, 2019, 1:32pm UTC](https://forum.crystal-lang.org/t/detecting-class-types/1090/11 "2019-09-04T13:32:07Z")

</div>

Actually, nevermind. The fix is really simple. I’ll push it later today.

---

<div class="post-metadata">

**Author:** ![asterite](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/asterite/32/60_2.png) [@asterite](https://forum.crystal-lang.org/u/asterite)\
**Post date:** [September 5, 2019, 1:08am UTC](https://forum.crystal-lang.org/t/detecting-class-types/1090/12 "2019-09-05T01:08:21Z")

</div>

Here’s the fix: [https://github.com/crystal-lang/crystal/pull/8149](https://github.com/crystal-lang/crystal/pull/8149)

---

<div class="post-metadata">

**Author:** ![stakach](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/stakach/32/58_2.png) [@stakach](https://forum.crystal-lang.org/u/stakach)\
**Post date:** [September 5, 2019, 2:14am UTC](https://forum.crystal-lang.org/t/detecting-class-types/1090/13 "2019-09-05T02:14:00Z")

</div>

Woot! Thanks @asterite
