# \[Suggestion\] Recommend helper use \`Types.string\` over \`Types::Strict::String\`

**URL:** <https://discourse.dry-rb.org/t/suggestion-recommend-helper-use-types-string-over-types-string/932>\
**Category:** Ideas\
**Created:** [October 23, 2019, 10:21pm UTC](https://discourse.dry-rb.org/t/suggestion-recommend-helper-use-types-string-over-types-string/932 "2019-10-23T22:21:42Z")\
**Posts on this page:** 9\
**Page:** 1

<div class="post-metadata">

**Author:** ![patrickclery](https://yyz1.discourse-cdn.com/flex031/user_avatar/discourse.dry-rb.org/patrickclery/32/451_2.png) [@patrickclery](https://discourse.dry-rb.org/u/patrickclery)\
**Post date:** [October 23, 2019, 10:21pm UTC](https://discourse.dry-rb.org/t/suggestion-recommend-helper-use-types-string-over-types-string/932/1 "2019-10-23T22:21:42Z")

</div>

This was originally a [GitHub issue](https://github.com/dry-rb/dry-types/issues/361), now moved here for discussion

## User Story

As a developer I’d like to be able to access

- [] call type objects with class methods
- [] call class methods even when `Dry::Types` is aliased to `Types`

So the code is more readable, & less typing for the developer.

## Examples

```ruby
module Types
 include Dry.Types()
end
# old
drinking_age = Types::Integer.constrained(gt: 21)
# new
drinking_age = Dry::Types.integer.constrained(gt: 21)
# old
email = Dry::Types::String.constrained(
 format: /\A[\w+\-.]+@[a-z\d\-]+(\.[a-z]+)*\.[a-z]+\z/i
)
email = Dry::Types.string.constrained(
 format: /\A[\w+\-.]+@[a-z\d\-]+(\.[a-z]+)*\.[a-z]+\z/i
)

```

Not a huge difference, right? But once you’ve alias `Types`, your code changes:

```ruby
# from this
drinking_age = Dry::Types::Integer.constrained(gt: 21)
# to this
drinking_age = Types.integer.constrained(gt: 21)
# or if you need more specificity
drinking_age = Types.send('coerible.integer').constrained(gt: 21)

```

☝ 6 characters less, and more readable (in my opinion).

Here’s a working of how I updated my tinder\_client gem from using `Dry::Types['string']` to `Types.string`:

```diff
   class Updates < Dry::Struct
 
- attribute :blocks, Dry::Types['array'].of(Dry::Types['string'])
- attribute :deleted_lists, Dry::Types['array']
- attribute :goingout, Dry::Types['array']
- attribute :harassing_messages, Dry::Types['array']
- attribute :inbox, Dry::Types['array'] do
- attribute :_id, Dry::Types['string']
- attribute :match_id, Dry::Types['string']
- attribute :sent_date, Dry::Types['string']
- attribute :message, Dry::Types['string']
- attribute :to, Dry::Types['string']
- attribute :from, Dry::Types['string']
- attribute :created_date, Dry::Types['string']
- attribute :timestamp, Dry::Types['coercible.string']
+ attribute :blocks, Types.array.of(Types.string)
+ attribute :deleted_lists, Types.array
+ attribute :goingout, Types.array
+ attribute :harassing_messages, Types.array
+ attribute :inbox, Types.array do
+ attribute :_id, Types.string
+ attribute :match_id, Types.string
+ attribute :sent_date, Types.string
+ attribute :message, Types.string
+ attribute :to, Types.string
+ attribute :from, Types.string
+ attribute :created_date, Types.string
+ attribute :timestamp, Types.send('coercible.string')
     end
   end
 end

```

## Resources

The way I’m able to accomplish this was by taking the existing snippet from the `dry-types` docs to create a `Types` module, then add all the type keys as methods:

### `spec_helper.rb`

`````ruby
require 'dry-types'
require 'dry-struct'

# This automatically transforms keys to symbols when you pass Structs a hash
# that has string keys
class Dry::Struct
  transform_keys(&:to_sym)
end

# The following allow you to access types with Types.<type reference>
#
# ```ruby
# email = Types.string.constrained(
# format: /\A[\w+\-.]+@[a-z\d\-]+(\.[a-z]+)*\.[a-z]+\z/i
# )
# drinking_age = Types.integer.constrained(gt: 21)
# ````
module Types
  include Dry.Types()

  class << self

    # @param String The key of the Dry::Type -- see Dry::Types.type_keys
    def [] (type_key)
      Dry::Types[type_key]
    end

    # This aliases all the Dry::Types keys as class methods
    Dry::Types.type_keys.each do |method_name|
      define_method method_name do
        Dry::Types[method_name]
      end
    end
  end

end

`````

@flash-gordon @solnic please take a look thanks 🙂

---

<div class="post-metadata">

**Author:** ![patrickclery](https://yyz1.discourse-cdn.com/flex031/user_avatar/discourse.dry-rb.org/patrickclery/32/451_2.png) [@patrickclery](https://discourse.dry-rb.org/u/patrickclery)\
**Post date:** [October 23, 2019, 10:24pm UTC](https://discourse.dry-rb.org/t/suggestion-recommend-helper-use-types-string-over-types-string/932/2 "2019-10-23T22:24:04Z")

</div>

I have PR on this: [https://github.com/dry-rb/dry-types/pull/362](https://github.com/dry-rb/dry-types/pull/362)

It’s purely documentation PR

Yes I now realize I should’ve discussed first 🙂 but it is what it is.

---

<div class="post-metadata">

**Author:** ![solnic](https://yyz1.discourse-cdn.com/flex031/user_avatar/discourse.dry-rb.org/solnic/32/779_2.png) [@solnic](https://discourse.dry-rb.org/u/solnic)\
**Post date:** [October 24, 2019, 7:48am UTC](https://discourse.dry-rb.org/t/suggestion-recommend-helper-use-types-string-over-types-string/932/3 "2019-10-24T07:48:07Z")

</div>

Defining a module for type constants is **the way** of using the library. For convenience we can have something like `require "dry/types/defaults"` (or something) that would define it for you, if doing it manually is an annoying process. The reason why we use constants is because types should be globally accessible constants. It is also very common to define your own types, and for that you want your own namespace too. Using built-in `Dry::Types` container is actually reserved for some lower-level tasks, like generating types programatically based on some identifiers.

If your really prefer to use methods you can create your own extension, but it will only work with built-in types and defining your own types would make things inconsistent.

I also noticed some confusion so let me clarify:

> call class methods even when `Dry::Types` is aliased to `Types`

Including `Dry.Types` into your `Types` module **does not create any alias** and `Dry::Types` **does not include type constants**. so ie `Dry::Types::String` does not exist and if you try to access types like that, you will get an exception:

```auto
irb(main):001:0> Dry::Types::String
NameError: dry-types does not define constants for default types.
You can access the predefined types with [],
 e.g. Dry::Types["integer"] or generate a module with types using Dry.Types()

```

When you include `Dry.Types` it will assign built-in types to constants for you, that’s all it does. This way you have a very clean types namespace w/o any unexpected modules. This is important because dealing with lots of common constant names actually can lead to weird issues if things are not properly namespaced and isolated.

Like I mentioned, if convenient usage in places like one-off scripts or console is the goal, then we can add a helper that defines the default types namespace for you, ie:

```ruby
require "dry/types/defaults"

# ...and `Types` is already available

```

Like Nikita said in the related issue, this isn’t something we’d want to have in the library because it would only create confusion about the recommended usage and on top of it it would make accessing types incosistent (methods for built-in types vs accessing custom types in a different way).

Cheers,  
P.

---

<div class="post-metadata">

**Author:** ![patrickclery](https://yyz1.discourse-cdn.com/flex031/user_avatar/discourse.dry-rb.org/patrickclery/32/451_2.png) [@patrickclery](https://discourse.dry-rb.org/u/patrickclery)\
**Post date:** [October 24, 2019, 10:03pm UTC](https://discourse.dry-rb.org/t/suggestion-recommend-helper-use-types-string-over-types-string/932/4 "2019-10-24T22:03:55Z")

</div>

> [@solnic](#):
>
> When you include `Dry.Types` it will assign built-in types to constants for you, that’s all it does. This way you have a very clean types namespace w/o any unexpected modules. This is important because dealing with lots of common constant names actually can lead to weird issues if things are not properly namespaced and isolated.

That really puts it into perspective for me, thank you. I get what you’re saying: these are meant to be globally accessible constants, and not just a helper layer between that. With that in mind, adding these as methods is not proper organization, and it should be added as a helper, either globally or in whatever context.

> [@solnic](#):
>
> For convenience we can have something like `require "dry/types/defaults"` (or something) that would define it for you, if doing it manually is an annoying process.

Agree 💯 - a helper would make it easy, and only requires 1 LoC to get started: `require "dry/types/defaults"`

* * *

Regarding the use of `Types.string` - personally I love this because it imitates `dry-struct` another favorite dry library of mine 🙂 Do you feel this syntax is too strong a change and is my personal preference? (It could be, I’m genuinely curious)

When I refactored my code from `Dry::Types['strict.string']` to `Types.string` etc, it was absolutely more readable. If there’s any kind of interference that breaks any functionality with dry-types currently, then I can completely understand why it would be a bad change.

Thanks for the reply, looking forward to the feedback

---

<div class="post-metadata">

**Author:** ![solnic](https://yyz1.discourse-cdn.com/flex031/user_avatar/discourse.dry-rb.org/solnic/32/779_2.png) [@solnic](https://discourse.dry-rb.org/u/solnic)\
**Post date:** [October 25, 2019, 7:21am UTC](https://discourse.dry-rb.org/t/suggestion-recommend-helper-use-types-string-over-types-string/932/5 "2019-10-25T07:21:22Z")

</div>

> [@patrickclery](#):
>
> Agree 💯 - a helper would make it easy, and only requires 1 LoC to get started: `require "dry/types/defaults"`

Yeah I guess we should just do it. WDYT @flash-gordon?

> [@patrickclery](#):
>
> Regarding the use of `Types.string` - personally I love this because it imitates `dry-struct` another favorite dry library of mine 🙂 Do you feel this syntax is too strong a change and is my personal preference? (It could be, I’m genuinely curious)
> 
> When I refactored my code from `Dry::Types['strict.string']` to `Types.string` etc, it was absolutely more readable. If there’s any kind of interference that breaks any functionality with dry-types currently, then I can completely understand why it would be a bad change.

It’s not really recommended to use `Dry::Types['strict.string']` and a custom types module should be used instead. For me `Types::String` makes more sense than `Types.string` because I want to refer to a type explicitly, not through a method call, because there’s literally no reason why it should be hidden behind a method call.

---

<div class="post-metadata">

**Author:** ![patrickclery](https://yyz1.discourse-cdn.com/flex031/user_avatar/discourse.dry-rb.org/patrickclery/32/451_2.png) [@patrickclery](https://discourse.dry-rb.org/u/patrickclery)\
**Post date:** [October 25, 2019, 10:12pm UTC](https://discourse.dry-rb.org/t/suggestion-recommend-helper-use-types-string-over-types-string/932/6 "2019-10-25T22:12:38Z")

</div>

@solnic Gotcha. I’m heavily in RSpec doing TDD-style stuff where the syntax is flat like `expect(something).to be Something.something`. I started using dry-struct because of the constant `['']` typing and refactoring, and now with Types shortened it’s even faster - but that’s just _my_ style.

Please please pleaaase let me try a PR for the helper if @flash-gordon is on board 🙂

---

<div class="post-metadata">

**Author:** ![flash-gordon](https://yyz1.discourse-cdn.com/flex031/user_avatar/discourse.dry-rb.org/flash-gordon/32/494_2.png) [@flash-gordon](https://discourse.dry-rb.org/u/flash-gordon)\
**Post date:** [October 26, 2019, 9:13am UTC](https://discourse.dry-rb.org/t/suggestion-recommend-helper-use-types-string-over-types-string/932/7 "2019-10-26T09:13:35Z")

</div>

I like the idea with `'dry/types/defaults'`. However, we should add a warning this to be used in application code, not in libraries.

---

<div class="post-metadata">

**Author:** ![patrickclery](https://yyz1.discourse-cdn.com/flex031/user_avatar/discourse.dry-rb.org/patrickclery/32/451_2.png) [@patrickclery](https://discourse.dry-rb.org/u/patrickclery)\
**Post date:** [October 28, 2019, 11:43pm UTC](https://discourse.dry-rb.org/t/suggestion-recommend-helper-use-types-string-over-types-string/932/8 "2019-10-28T23:43:57Z")

</div>

@flash-gordon @solnic

Supposing that the docs are updated to recommend `require 'dry/types/defaults'` – What are everyone’s thoughts on which style to use in examples throughout the docs?

- `Types.string`
- `Dry::Types['strict.string']`
- `Types['strict.string']`
- `Types::Strict::String`

Also, currently it’s using `Dry::Types['string']` - notice that it’s omitting “strict.” and also adding `Dry::`

- Should the examples all be implicit, or explictly strict?

Lmk your thoughts thanks 🙂

---

<div class="post-metadata">

**Author:** ![flash-gordon](https://yyz1.discourse-cdn.com/flex031/user_avatar/discourse.dry-rb.org/flash-gordon/32/494_2.png) [@flash-gordon](https://discourse.dry-rb.org/u/flash-gordon)\
**Post date:** [October 30, 2019, 9:55pm UTC](https://discourse.dry-rb.org/t/suggestion-recommend-helper-use-types-string-over-types-string/932/9 "2019-10-30T21:55:32Z")

</div>

`Types::String` would be fine for strict types and qualified namespaces for other kinds, like `Types::Coercible::Integer` etc
