Skip to content

Commit 8ba671e

Browse files
Who-is-PSamanabiy
authored andcommitted
chore: Add ESLint rule to prevent unsafe in operator checks (#4312)
1 parent 34c9316 commit 8ba671e

File tree

22 files changed

+39
-0
lines changed

22 files changed

+39
-0
lines changed

eslint.config.mjs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,13 @@ export default tsEslint.config(
115115
'line',
116116
[' Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.', ' SPDX-License-Identifier: Apache-2.0'],
117117
],
118+
'no-restricted-syntax': [
119+
'error',
120+
{
121+
selector: 'BinaryExpression[operator="in"][left.type="Literal"]',
122+
message: 'Prefer a type guard function with `keyof` instead of raw `in` checks.',
123+
},
124+
],
118125
'no-warning-comments': 'warn',
119126
'simple-import-sort/imports': [
120127
'error',

pages/alert/runtime-content.page.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ awsuiPlugins.alertContent.registerContentReplacer({
6969
!!(
7070
context.content &&
7171
typeof context.content === 'object' &&
72+
// eslint-disable-next-line no-restricted-syntax -- Runtime duck typing on unknown plugin content
7273
'props' in context.content &&
7374
typeof context.content.props.children === 'string' &&
7475
context.content.props.children.match('Access denied')

pages/multiselect/multiselect.sync.example.page.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ const onBlur = () => {
3030
};
3131

3232
export default function MultiselectPage() {
33+
// eslint-disable-next-line no-restricted-syntax -- Page demo: filtering option groups from flat options
3334
const selectableOptions = options.filter(o => !o.disabled && !('options' in o));
3435
const [selectedOptions1, setSelectedOptions1] = React.useState<MultiselectProps.Options>([
3536
selectableOptions[10],

pages/multiselect/permutations.page.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,9 @@ const options: MultiselectProps.Options = [
3232
{ value: 'more4', label: 'More options with very long texts 4' },
3333
];
3434

35+
// eslint-disable-next-line no-restricted-syntax -- Page demo: option/group discrimination
3536
const _getGroupOptions = (option: OptionDefinition | OptionGroup) => ('options' in option ? option.options : [option]);
37+
// eslint-disable-next-line no-restricted-syntax -- Page demo: option/group discrimination
3638
const _getFirstChild = (option: OptionDefinition | OptionGroup) => ('options' in option ? option.options[0] : option);
3739

3840
const permutations = createPermutations<MultiselectProps & { inlineTokens?: boolean }>([

pages/multiselect/screenshot.page.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ export default function MultiselectPage() {
5050
...option,
5151
disabled: false,
5252
options:
53+
// eslint-disable-next-line no-restricted-syntax -- Page demo: option/group discrimination
5354
'options' in option && option.options.length
5455
? option.options.map(childOption => ({ ...childOption, disabled: false }))
5556
: undefined,
@@ -59,6 +60,7 @@ export default function MultiselectPage() {
5960
? groupedOptions
6061
: groupedOptions.reduce(
6162
(previousValue: MultiselectProps.Options, currentValue: MultiselectProps.Option) =>
63+
// eslint-disable-next-line no-restricted-syntax -- Page demo: option/group discrimination
6264
'options' in currentValue && (currentValue as OptionGroup).options?.length
6365
? [...previousValue, ...(currentValue as OptionGroup).options]
6466
: [...previousValue, currentValue],

pages/table/grouped-table/grouped-table-data.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ export interface GroupDefinition {
5959
}
6060

6161
export function isGroupRow(row: TransactionRow): row is TransactionRowGroup {
62+
// eslint-disable-next-line no-restricted-syntax -- Union type discrimination for TransactionRow
6263
return 'children' in row;
6364
}
6465

src/__a11y__/a11y-page-object.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ export default class A11yPageObject extends BasePageObject {
7979
runOptions
8080
);
8181

82+
// eslint-disable-next-line no-restricted-syntax -- Test utility: runtime check on axe result
8283
if ('error' in response) {
8384
throw response.error;
8485
}

src/app-layout/runtime-drawer/index.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,10 +87,12 @@ function mapRuntimeHeaderActionsToHeaderActions(
8787
return runtimeHeaderActions.map(runtimeHeaderAction => {
8888
return {
8989
...runtimeHeaderAction,
90+
// eslint-disable-next-line no-restricted-syntax -- Runtime plugin API: property not in TS type
9091
...('iconSvg' in runtimeHeaderAction &&
9192
runtimeHeaderAction.iconSvg && {
9293
iconSvg: convertRuntimeTriggerToReactNode(runtimeHeaderAction.iconSvg),
9394
}),
95+
// eslint-disable-next-line no-restricted-syntax -- Runtime plugin API: property not in TS type
9496
...('pressedIconSvg' in runtimeHeaderAction &&
9597
runtimeHeaderAction.pressedIconSvg && {
9698
iconSvg: convertRuntimeTriggerToReactNode(runtimeHeaderAction.pressedIconSvg),

src/app-layout/visual-refresh-toolbar/state/use-app-layout.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,7 @@ export const useAppLayout = (
254254
return;
255255
}
256256

257+
// eslint-disable-next-line no-restricted-syntax -- postMessage validation: runtime data shape check
257258
if (!('payload' in message && 'id' in message.payload)) {
258259
metrics.sendOpsMetricObject('awsui-widget-drawer-incorrect-payload', {
259260
type: message.type,

src/error-boundary/__tests__/error-boundary.test.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ function Recursive({ boundaries }: { boundaries: BoundaryTestSpec[] }) {
4343
</div>
4444
),
4545
};
46+
// eslint-disable-next-line no-restricted-syntax -- Test: runtime error type check
4647
return 'onError' in props ? <ErrorBoundary {...props} /> : <BuiltInErrorBoundary {...props} />;
4748
}
4849

@@ -436,6 +437,7 @@ describe('error propagation and suppressNested', () => {
436437
])('suppressNested and suppressible: $description', ({ boundaries, expected }) => {
437438
render(<Recursive boundaries={boundaries} />);
438439
expect(findHeader().getElement()).toHaveTextContent(expected);
440+
// eslint-disable-next-line no-restricted-syntax -- Test: runtime error type check
439441
for (const { onError, errorBoundaryId } of boundaries.filter(b => 'onError' in b)) {
440442
if (errorBoundaryId === expected) {
441443
expect(onError).toHaveBeenCalledWith({
@@ -546,6 +548,7 @@ describe('built-in error boundaries', () => {
546548
])('suppressing built-in boundaries, $description', ({ boundaries, expected }) => {
547549
render(<Recursive boundaries={boundaries} />);
548550
expect(findBoundary()!.getElement().parentElement!.textContent).toBe(expected);
551+
// eslint-disable-next-line no-restricted-syntax -- Test: runtime error type check
549552
for (const { onError, errorBoundaryId } of boundaries.filter(b => 'onError' in b)) {
550553
if (errorBoundaryId && expected.includes(errorBoundaryId)) {
551554
expect(onError).toHaveBeenCalledWith({

0 commit comments

Comments
 (0)