Skip to content

Instantly share code, notes, and snippets.

@bradkovach
Created November 18, 2024 17:45
Show Gist options
  • Save bradkovach/3f657d20aa3bd538f6536b0b874bad4e to your computer and use it in GitHub Desktop.
Save bradkovach/3f657d20aa3bd538f6536b0b874bad4e to your computer and use it in GitHub Desktop.
Annotated, useful eslint no-restricted-syntax rules

These examples of the eslint no-restricted-syntax rule that demonstrate how you can use ESQuery to pin down structural code problems. While these examples are written for modern ESLint 8+ Flat Configuration, they are easily adapted to the older eslintrc format.

I hope to keep this updated as I develop more rules.

Some helpful tools for developing your own selectors for no-restricted-syntax:

export default [
{
"rules": [
"no-restricted-syntax": [
"error",
/*
Ensure Angular services are implementations of Interfaces
DEMO: https://typescript-eslint.io/play/#ts=5.4.3&fileType=.ts&code=LAKAAgkgdgVgpgYwC4EMBGAbOAKA3qAAgIAcAnAewDcBLAEzlugC4CByC8pV0AXwEpQcAB7FypJAQQYUAZxkEAsgE9olFBjoBlOKRoI4BXAR6hBIsROpQkOgGYp9Bbbur7oN0vcdGTIUJFhEVEwcfBAiMio6BmY2Di5eARBhUXFJaTlFJQA1dS0dPQNqAFtiLGK4a3lnQvc7BwMfIA&eslintrc=N4KABGBEBOCuA2BTAzpAXGUEKQHYHsBaaFAF2gEsBjUxAE0OQE9dSBDAD3TAG1xsciaNHzRIAGn4CIWaQMgBbFMjYBzRN0gBlIQDdqKMMgAW%2BBHTAUFAByRLWYNrkushAMzZVEAOglS5UMiISDSimgCiHNaipABybEp0ACKIVPBs0GykFPjOAHxgAMLpyMgpaRlZObhoxmzIABTlolmiPIhRJKXV3lRs8Eg%2BuAmIALwAkrgAVqnsAEZIALoAlDxWtoj2pMjeSLiqpMajAAyLkP4CAL6SAov8lyCXQA&tsconfig=N4KABGBEDGD2C2AHAlgGwKYCcDyiAuysAdgM6QBcYoEEkJemy0eAcgK6qoDCAFutAGsylBm3TgwAXxCSgA&tokens=false
*/
{
"message": "Services should implement an interface.",
"selector": ""
// find exports
+"ExportNamedDeclaration" +
// and then select ClassDeclaration where...
"> ClassDeclaration:has("
// ... it has the Injectable decorator ...
+" Decorator[expression.callee.name=Injectable]"
// and has no implements Expressions
+ ")[implements.length=0]"
,
},
/**
Use non-literal keys for sessionStorage and localStorage
DEMO: https://typescript-eslint.io/play/#ts=5.4.3&fileType=.ts&code=PTAEEkDkDUEEBlwBFQHcCWA7AJge1QHQA2uAxgIZEDKALrgE7kDmApgQBSs3g0sC2AHwDOLbr0H1%2BuAG4se-AJSgs0suRrpcmIQFgAUBhz5ia6nUasCXeX3YByckOwAzOwoDc%2Bw3kIkKZhmY2ETF%2Be0cXOwAaUDsARgAmAGYAFjdPAywfE39aQMtJPhk5cXCnVw99fRAIGARkNCzjESEhTUw8izZOURthXvEBQuKbJRU1DS1dTKNCFratTqCrAbCHcvSvJrmWVvalyxCbMsiY%2BOS0ypns%2Bf3zZeHZY-XIq%2BqwKDhEFD9KA%2B7rIMjoNHiVFMpMKoKJNtPpfgEuitQrYXhUMvD-gRgWsIq4zolUps9Bj7gUpE9Sqiie9al8GrdFqSAatBNiJOSwXwxpCJu1pgyOkykc9cUSBZi2Sc8bECZcMuKhaCRRs3noWJgAK58UD-ADSLAAnqAAN76UCgXGgAC8sVF%2BgAvlU9DU6ShvMYSflmcj%2Bj6leJuVD1HytrMcn8hYCwnrDQRcVd3b5TBKWewYwa4%2BV8RciYnwwiHhzjunMy5VS76m7tljdgtBV6OFHWSyhkWAxCgzDpnmFQ2m2mmfqM-GMj3a3cG5KS7js4SE9Xe4j-dHB7GR06K99QJ7ET0fWzW0UKeDxtCQ8Tk5HU9Pylcd8sp6vh1mZTm75eG8vbDeyxkaa7QEXZY9z6A8v0DXkpn0IDLH7H9nCuGDgmvJ9S2cWc5Wg8dGU-NsVy9Ic0I8IA&eslintrc=N4KABGBEBOCuA2BTAzpAXGUEKQHYHsBaaFAF2gEsBjUxAE0OQE9dSBDAD3TAG1xsciaNHzRIAGn4CsA7JAC2KZGwDmibpACqyRGDZgCuQvAq1obeGABuF2LoAUiXLHniwVfLmSlrbSmwAjJDdvNlJqd3g2ZGQwAAcROKFSJgBKMAAzUTAAa0QmWIpcMB0Yik8AZVJRVV02XDoweHwqCyqatQA6CSlZSB0kGlENAGELeABRDgSlctweVvgkRE6UpIBeAFlEeQChKZmyzwBdHj8VFydSZE6ABlWmDYAZUyELU8XlzoT8JOgUzq4NiKdYAejUpAAkrR5AAfHRQmGwkjyfBWRDQnag45oeRhKgACxQ9gW40QK3wAQAVogaN9EskmIDgYgwc1Fu1zGp4bNKtUuYhseJSUtyZ1KTS6WtWZC6FcKBkKEIPmSKdTaaRmSDQey2vzajyjrhObVsekAHxgF5mCxoRXQbyEQkUeB0SC9CAAX16x343s9QA&tsconfig=N4KABGBEDGD2C2AHAlgGwKYCcDyiAuysAdgM6QBcYoEEkJemy0eAcgK6qoDCAFutAGsylBm3TgwAXxCSgA&tokens=false
*/
{
"message": "Use a non-literal value (enum, const variable, static class property) for keys in sessionStorage and localStorage.",
"selector": ""
// find a CallMember on a MemberExpression...
+ "CallExpression[callee.type=MemberExpression]"
// ... where the first argument is a Literal (such as a string or number)
+ "[arguments.0.type=Literal]"
// ... where the property is named getItem OR setItem OR removeItem ...
+ "[callee.property.name=/getItem|setItem|removeItem/]"
// ... AND where localStorage OR sessionStorage EITHER ..
+ ":matches("
// ... is called as a window MemberExpression ... [such as window.localStorage.getItem('x')]
+ " [callee.object.property.name=/localStorage|sessionStorage/],"
// ... OR is called with implicit window global ... [such as localStorage.getItem('x')]
+ " [callee.object.type=Identifier][callee.object.name=/localStorage|sessionStorage/]"
// and then select the Literal to highlight as the error
+ ") > Literal:first-child"
}
]
]
}
]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment