-
-
Notifications
You must be signed in to change notification settings - Fork 2.8k
New rule: avoid assertions if you can just use ! #1922
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
My question would be "why are you using a non-null assertion at all?" |
Because the DOM.
Or in this specific case: document.querySelector('[data-channel]')!.dataset.channel
// string | undefined ^ forgive this, demo only I just selected by |
How would you write a general rule which allows |
This is the point - the types must be defined based on supporting every single query-case, and have no context on how you're actually querying or consuming them. This is a common problem in every typed language when it comes to boundaries between a query language that generates no types. There is just some weirdness that needs to be worked around with non-null assertions or similar. That, or you add always-true checks into the code to correct the code. I hate it, but the alternative is ad-hoc, automatic generation of types based on a separate build chain, which is a lot of work. As another example, we have similar problems in this project with ESLint selectors and types - eg: const selectors = {
'TSNonNullExpression > TSNonNullExpression'(node: TSESTree. TSNonNullExpression) {
// guaranteed by the selector, but currently impossible to represent in types
const parent = node.parent as TSESTree.TSNonNullExpression;
},
'VariableDeclarator[init]'(node: TSESTree.VariableDeclarator) {
// selector guarantees non-null, but is hard to nicely represent in types
const init = node.init!;
},
// the only way to properly type this at declaration time
'VariableDeclarator[init]'(node: TSESTree.VariableDeclarator & {
init: NonNullable<TSESTree.VariableDeclarator['init']>;
}) {
const init = node.init; // correctly non-nullish
},
// or solve it with runtime checks
'VariableDeclarator[init]'(node: TSESTree.VariableDeclarator) {
/* istanbul ignore if */ if (node.init == null) {
return;
}
const init = node.init;
},
'VariableDeclarator[init]'(node: TSESTree.VariableDeclarator) {
const init = nullthrows(node.init, 'found no init on VariableDeclarator');
},
}
function nullthrows<T>(thing: T, msg: string): NonNullable<T> {
if (thing == null) { throw new Error(msg) }
return thing;
}
quick-and-dirty-very-rough-and-untested-pseudocode: return {
'TSAsExpression, TSTypeAssertion'(node) {
const baseType = getTypeAtLocation(node.expression);
if (!isUnionType(baseType) || !containsNullishTypes(baseType)) {
// non-nullish and non-union types cannot be non-null asserted on
return;
}
const assertedType = getTypeAtLocation(node.expression);
if (containsNullishTypes(baseType)) {
// asserting from string | null | undefined to string | null is fine
return;
}
for (const base of baseType.filter(nullishTypes)) {
if (!assertedType.contains(base)) {
return; // asserted type is not an exact NonNullable conversion
}
}
context.report('use a ! instead');
},
} |
Duplicate of #202. |
Yup! The new rule |
Uh oh!
There was an error while loading. Please reload this page.
I saw many newcomers (including myself) occasionally solving this error with assertions. For example:
Can be also written as:
The assertion is unnecessary, longer and less safe
The text was updated successfully, but these errors were encountered: