Fala, Gelson! Tudo bem?
São coisas diferentes mesmo.
O ponto de exclamação (!) indica ao compilador que não existe a possibilidade daquela variável ser null ou undefined. Acontece de às vezes o compilador não ser capaz de prever um comportamento, ou se você usar alguma biblioteca que não está bem tipada, o compilador não tem certeza do que pode chegar na variável. Assim, o ! é útil para esses casos. Se quiser saber mais sobre, o nome desse operador é Non-null Assertion Operator.
Já no caso do ponto de interrogação (?), é quando queremos acessar a propriedade de um objeto que pode ou não ser nulo. Diferente do !, aqui nós não assumimos responsabilidade nenhuma, na verdade, tiramos de nós a responsabilidade de garantir que o objeto exista. Porque quando usado, em tempo de execução, se o objeto existir, a propriedade é acessada, se o objeto não existir (null ou undefined), a expressão não gera um erro, apenas retorna undefined. O nome desse operador é Optional Chaining Operator. Seria similar a fazer o seguinte:
if (obj) {
return obj.propriedade;
} else {
return undefined;
}
Espero que ajude!