Sorbet: Can't provide concrete to generic extending class

Created on 23 Aug 2019  ·  4Comments  ·  Source: sorbet/sorbet

Input

→ View on sorbet.run

class Parent
  extend T::Sig
  extend T::Generic

  Elem = type_member

  sig { returns(Elem) }
  def object
    # to avoid actually writing the code
    T.unsafe(1)
  end
end

class Child < Parent[SomeClass]
  def print_object
    T.reveal_type(object)
  end
end

Observed output

editor.rb:16: Superclasses must only contain constant literals https://srb.help/4002
    16 |class Child < Parent[SomeClass]
                      ^^^^^^^^^^^^^^^^^
Errors: 1

There are some other errors, but they're because the inheritance didn't work

Expected behavior

Ideally, I'd like to be able to fill in the generic where I'm extending. Maybe there is another way to do this, but if I know what the underlying method is going to return (for example, in graphql-ruby, the object method returns a certain ApplicationRecord class)

I understand why the error exists, _but_ it seems like you can never extend from a class that uses generics, and provide the type for the generic.

In flow (JS) for instance, with React, you extend the Component class and provide the type for Props and State.

type Props = { id: string }
class MyComponent extends React.Component<Props> {
  someMethod() {
    console.log(this.props.id) // some string
  }
}

bug unconfirmed

All 4 comments

This already exists, you're just using the wrong syntax:

https://sorbet.run/#%23%20typed%3A%20true%0A%0Aclass%20Parent%0A%20%20extend%20T%3A%3ASig%0A%20%20extend%20T%3A%3AGeneric%0A%0A%20%20Elem%20%3D%20type_member%0A%0A%20%20sig%20%7B%20returns(Elem)%20%7D%0A%20%20def%20object%0A%20%20%20%20%23%20to%20avoid%20actually%20writing%20the%20code%0A%20%20%20%20T.unsafe(1)%0A%20%20end%0Aend%0A%0Aclass%20SomeClass%3B%20end%0Aclass%20Child%20%3C%20Parent%0A%20%20Elem%20%3D%20type_member(fixed%3A%20SomeClass)%0A%20%20%23%20should%20have%20a%20%60something%60%20method%20that%20returns%20an%20integer%0A%20%20def%20print_object%0A%20%20%20%20T.reveal_type(object)%0A%20%20end%0Aend

Note that generics are an intentionally undocumented feature because the experience of using them isn't great yet, so we are unlikely to document this feature in the near future.

Thanks for that!

I understand not wanting to document the generic system, but:

  • these types are used in a bunch of places that I've seen (I'd seen fixed, upper, and lower, and had no idea what they did or meant)
  • ruby being ruby almost _requires_ generics to make the system usable.

I'll use the advice you've given, but it'd be cool to see _some_ docs around it. The metaprogramming has very average DX, but they've got some rudimentary docs. Doesn't have to be complete, just, like, something.

@deecewan I'm not sure if you figured this out but AFAICT:

upper == covariant
lower == contravariant
fixed == invariant

Glad I found this because I was also not sure on the correct way to achieve this. Looking forward to this becoming official and documented 😍

Was this page helpful?
0 / 5 - 0 ratings

Related issues

paracycle picture paracycle  ·  3Comments

julius-stripe picture julius-stripe  ·  3Comments

viraptor picture viraptor  ·  4Comments

rafaelcgs10 picture rafaelcgs10  ·  3Comments

TobiasBales picture TobiasBales  ·  3Comments