» This Is Not the Type You're Looking For
In the example above, the user has passed "two"
, a String
, as an argument to the Array#first
method, which expects a type Integer
. When this happens, Ruby raises TypeError
to alert the user that it can't use the type of object that was passed in to produce a meaningful result.
Another example of TypeError
in Ruby is when using many of the built-in conversion functions to convert one type to another. When the object passed to each function cannot be automatically converted to the correct type, Ruby raises TypeError
:
Complex(nil) # => TypeError
Hash([1, 2, 3]) # => TypeError
Integer(nil) # => TypeError
Rational(nil) # => TypeError
» How To Use TypeError in Your Ruby Code
Let's take a look at a hypothetical method which requires a special kind of type in order to return a valid result:
CREATURES = ['NoMethodError', 'TypeError', 'ArgumentError', 'RuntimeError']
def print_random(number)
number.times do
puts CREATURES.sample
end
end
The print_random
method receives one argument, number
, which tells the method how many times to print a random creature. This works beautifully when called with an Integer
:
irb(main):012:0> print_random(3)
TypeError
ArgumentError
TypeError
=> 3
Unfortunately, we get a much more confusing result when calling the method with an unexpected argument, nil
:
irb(main):015:0> print_random(nil)
NoMethodError: undefined method `times' for nil:NilClass
A solution to this problem could be to explicitly convert the object to an Integer
before calling #times
on it:
def print_random(number)
number.to_i.times do
puts CREATURES.sample
end
end
Now, when we call print_random
with nil
, we at least don't get an error:
irb(main):034:0> print_random(nil)
=> 0
That's not a very meaningful result either, though. nil
doesn't give print_random
enough information to do what the user asked—print a random list of creatures. It would be much better to raise an exception so that the user can correct their mistake. TypeError
to the rescue (no pun intended ;))!
def print_random(number)
raise TypeError, "expected an Integer, got #{number.class.name}" unless number.is_a?(Integer)
number.times do
puts CREATURES.sample
end
end
Now, calling our method with nil
produces a helpful error which tells us exactly what we did wrong:
irb(main):041:0> print_random(nil)
TypeError: expected an Integer, got NilClass
While this method is improved with an explicit type check, we can make it even more robust while providing the same feedback by using one of the type conversion functions that we mentioned earlier. Let's take a look at an alternate implementation:
def print_random(number)
Integer(number).times do
puts CREATURES.sample
end
end
Now our method can be called with any object that supports implicit conversion to an Integer
, and it still raises TypeError
when it can't do the conversion:
irb(main):012:0> print_random(2)
TypeError
ArgumentError
=> 2
irb(main):012:0> print_random("1")
RuntimeError
=> 1
irb(main):041:0> print_random(nil)
TypeError: can't convert nil into Integer