Like 0 or "", nil is untyped. Go has both typed and untyped constants. 0 also is untyped, which is why you can write x==0 regardless of whether x is an int32 or an int64.
Unlike 0 or "", nil does not have a default type. The default type of a constant is the type that it is implicitly converted to when a type is required. For example, "x := 0" declares a new value x with the value 0, but 0 is untyped. Untyped integer constants have a default type of int, so x is given the type int.
Since nil does not have a default type, it's an error to write "x := nil".
You can declare a typed constant with the value 0 or nil. int32(0) is a constant with type int32 and the value 0. (* int32)(nil) is a constant with the type pointer-to-int32 and the value nil.
Variables of an interface type can hold any value that satisfies the interface. For example, a value of type io.Reader can hold any value that has a Read method with the appropriate signature.
The common confusion about nil occurs because you can compare an interface value to a non-interface value.
var b *bytes.Buffer // b is a variable with type *bytes.Buffer
var r io.Reader // r is a variable with type io.Reader
r = b // r now holds a value with type *bytes.Buffer and value nil
b == nil // true: b is nil
r == b // true: r and b are equal
r == (io.Reader)(nil) // false: r is not a nil io.Reader
r == (*bytes.Buffer)(nil) // true: r contains a nil *bytes.Buffer
r == nil // false: r is not a nil io.Reader
The confusing part is that last line. How can r not be nil, when it contains a nil value? And the reason is hopefully apparent from the previous two lines: It depends on the type of "nil".Whether you find this a wart in the language or not, this is not a case of nil being either typed in theory or untyped in practice. The predeclared identifier "nil" is an untyped constant.