Avoid non-null assertion operator (!) and use type-safe alternatives instead
The non-null assertion operator (!) is deprecated in modern TypeScript because it bypasses type safety and can lead to runtime errors.
!undefined or null errors at runtime?.)Bad:
const userName = user!.profile!.name;
Good:
const userName = user?.profile?.name;
??)Bad:
const value = config!.timeout;
Good:
const value = config?.timeout ?? 5000;
Bad:
function processUser(user: User | null) {
console.log(user!.name);
}
Good:
function processUser(user: User | null) {
if (user !== null) {
console.log(user.name);
}
}
Bad:
function getUserEmail(userId: number): string {
const user = findUser(userId);
return user!.email;
}
Good:
function getUserEmail(userId: number): string | null {
const user = findUser(userId);
if (!user) {
return null;
}
return user.email;
}
Bad:
function handleValue(value: unknown) {
return (value as User)!.name;
}
Good:
function isUser(value: unknown): value is User {
return typeof value === 'object' && value !== null && 'name' in value;
}
function handleValue(value: unknown) {
if (isUser(value)) {
return value.name;
}
throw new Error('Invalid user');
}
in OperatorBad:
function process(obj: { data?: string }) {
console.log(obj.data!.toUpperCase());
}
Good:
function process(obj: { data?: string }) {
if ('data' in obj && obj.data !== undefined) {
console.log(obj.data.toUpperCase());
}
}
Bad:
const users: User[] = getUsers();
const firstUser = users[0]!;
Good:
const users: User[] = getUsers();
const firstUser = users.at(0);
if (firstUser) {
console.log(firstUser.name);
}
Good:
function assertIsDefined<T>(value: T): asserts value is NonNullable<T> {
if (value === undefined || value === null) {
throw new Error('Value must be defined');
}
}
function process(value: string | null) {
assertIsDefined(value);
console.log(value.toUpperCase());
}
Bad:
const button = document.getElementById('submit')!;
button.addEventListener('click', handler);
Good:
const button = document.getElementById('submit');
if (button) {
button.addEventListener('click', handler);
}
Or with assertion function:
function assertElement<T extends Element>(
element: T | null,
selector: string
): asserts element is T {
if (!element) {
throw new Error(`Element not found: ${selector}`);
}
}
const button = document.getElementById('submit');
assertElement(button, '#submit');
button.addEventListener('click', handler);
! Acceptable?Only in very rare cases where:
Even then, prefer assertion functions over !.
! in codebaseEnable strict checks:
{
"compilerOptions": {
"strict": true,
"strictNullChecks": true,
"noUncheckedIndexedAccess": true
}
}
Never use ! operator:
?. for optional chaining?? for default values