The command part of a timer will always be evaluated.
In the moment you start the timer, $chan will become e.g. %#chan. Then, in the moment the timer is executed, %#chan would be treated as a variable (i.e. evaluated to the value of that variable %#chan, which most likely is "nothing").
Note that it may be a security issue if you don't account for that extra evaluation (e.g. for #$chan_named_like_an_identifier).
Here are some additional infos and two methods for bypassing this "extra" evaluation. Suggestion:
on 1:JOIN:#: {
if ($nick != $me) {
.timer 1 2 msg $safe2($chan) Welcome $nick
}
}
alias safe2 { bset -t &a 1 $1 | return $!regsubex(safe, $bvar(&a,1-) ,/(\d+)(?: |$)/g,$chr(\1)) }