diff --git a/src/autosuggest/autosuggest-option.tsx b/src/autosuggest/autosuggest-option.tsx index ba7682acb0..71cc5603d3 100644 --- a/src/autosuggest/autosuggest-option.tsx +++ b/src/autosuggest/autosuggest-option.tsx @@ -108,9 +108,9 @@ const AutosuggestOption = ( ref: React.Ref ) => { const baseProps = getBaseProps(rest); - const useEntered = option.type === 'use-entered'; - const isParent = option.type === 'parent'; - const isChild = option.type === 'child'; + const useEntered = option?.type === 'use-entered'; + const isParent = option?.type === 'parent'; + const isChild = option?.type === 'child'; const { throughIndex, inGroupIndex, groupIndex } = getTestOptionIndexes(option) || {}; let optionContent; diff --git a/src/autosuggest/utils/utils.ts b/src/autosuggest/utils/utils.ts index 0cc6082f49..7bbe493266 100644 --- a/src/autosuggest/utils/utils.ts +++ b/src/autosuggest/utils/utils.ts @@ -5,7 +5,7 @@ import { AutosuggestItem } from '../interfaces'; type SearchableFields = 'value' | 'label' | 'description' | 'labelTag'; type SearchableTagFields = 'tags' | 'filteringTags'; -const isGroup = (option: AutosuggestItem) => option.type === 'parent'; +const isGroup = (option: AutosuggestItem) => option?.type === 'parent'; const popLastGroup = (options: AutosuggestItem[]) => { if (options.length) { diff --git a/src/button-dropdown/internal.tsx b/src/button-dropdown/internal.tsx index e616784397..b392d6282d 100644 --- a/src/button-dropdown/internal.tsx +++ b/src/button-dropdown/internal.tsx @@ -22,7 +22,7 @@ import { GeneratedAnalyticsMetadataButtonDropdownCollapse, GeneratedAnalyticsMetadataButtonDropdownExpand, } from './analytics-metadata/interfaces.js'; -import { ButtonDropdownProps, InternalButtonDropdownProps } from './interfaces'; +import { ButtonDropdownProps, InternalButtonDropdownProps, InternalItem } from './interfaces'; import ItemsList from './items-list'; import { useButtonDropdown } from './utils/use-button-dropdown'; import { isLinkItem } from './utils/utils.js'; @@ -180,18 +180,20 @@ const InternalButtonDropdown = React.forwardRef( const triggerId = useUniqueId('awsui-button-dropdown__trigger'); const triggerHasBadge = () => { + const groupKey: keyof ButtonDropdownProps.ItemGroup = 'items'; const flatItems = items.flatMap(item => { - if ('items' in item) { + if (groupKey in item) { return item.items; } return item; }); + const badgeKey: keyof InternalItem = 'badge'; return ( variant === 'icon' && !!flatItems?.find(item => { - if ('badge' in item) { - return item.badge; + if (badgeKey in item) { + return (item as InternalItem).badge; } }) ); diff --git a/src/button-group/item-element.tsx b/src/button-group/item-element.tsx index 8cc623e1c1..325dd7e538 100644 --- a/src/button-group/item-element.tsx +++ b/src/button-group/item-element.tsx @@ -103,7 +103,8 @@ const ItemElement = forwardRef( }; const onClickHandler = (event: CustomEvent) => { - const hasPopoverFeedback = 'popoverFeedback' in item && item.popoverFeedback; + const feedbackKey: keyof ButtonGroupProps.IconButton = 'popoverFeedback'; + const hasPopoverFeedback = feedbackKey in item && item.popoverFeedback; if (hasPopoverFeedback) { setTooltip({ item: item.id, feedback: true }); diff --git a/src/flashbar/collapsible-flashbar.tsx b/src/flashbar/collapsible-flashbar.tsx index 60d10a51e6..644ac35f1b 100644 --- a/src/flashbar/collapsible-flashbar.tsx +++ b/src/flashbar/collapsible-flashbar.tsx @@ -35,6 +35,8 @@ import { getFlashTypeCount, getItemColor, getVisibleCollapsedItems, + isRefObject, + isStackableItem, StackableItem, } from './utils'; @@ -214,11 +216,11 @@ export default function CollapsibleFlashbar({ items, style, ...restProps }: Inte // we need to use different, more custom and more controlled animations. const hasEntered = (item: StackableItem | FlashbarProps.MessageDefinition) => enteringItems.some(_item => _item.id && _item.id === item.id); - const hasLeft = (item: StackableItem | FlashbarProps.MessageDefinition) => !('expandedIndex' in item); + const hasLeft = (item: StackableItem | FlashbarProps.MessageDefinition) => !isStackableItem(item); const hasEnteredOrLeft = (item: StackableItem | FlashbarProps.MessageDefinition) => hasEntered(item) || hasLeft(item); const showInnerContent = (item: StackableItem | FlashbarProps.MessageDefinition) => - isFlashbarStackExpanded || hasLeft(item) || ('expandedIndex' in item && item.expandedIndex === 0); + isFlashbarStackExpanded || hasLeft(item) || (isStackableItem(item) && item.expandedIndex === 0); const shouldUseStandardAnimation = (item: StackableItem, index: number) => index === 0 && hasEnteredOrLeft(item); @@ -301,11 +303,7 @@ export default function CollapsibleFlashbar({ items, style, ...restProps }: Inte if (shouldUseStandardAnimation(item, index) && transitionRootElement) { if (typeof transitionRootElement === 'function') { transitionRootElement(el); - } else if ( - transitionRootElement && - typeof transitionRootElement === 'object' && - 'current' in transitionRootElement - ) { + } else if (isRefObject(transitionRootElement)) { (transitionRootElement as React.MutableRefObject).current = el; } } diff --git a/src/flashbar/non-collapsible-flashbar.tsx b/src/flashbar/non-collapsible-flashbar.tsx index 96cb0015b0..6c33ddb876 100644 --- a/src/flashbar/non-collapsible-flashbar.tsx +++ b/src/flashbar/non-collapsible-flashbar.tsx @@ -13,6 +13,7 @@ import { useFlashbar, useFlashbarVisibility } from './common'; import { TIMEOUT_FOR_ENTERING_ANIMATION } from './constant'; import { Flash } from './flash'; import { FlashbarProps, InternalFlashbarProps } from './interfaces'; +import { isRefObject } from './utils'; import styles from './styles.css.js'; @@ -121,11 +122,7 @@ export default function NonCollapsibleFlashbar({ items, i18nStrings, style, ...r // If there's a transition root element ref, update it too if (transitionRootElement && typeof transitionRootElement === 'function') { transitionRootElement(el); - } else if ( - transitionRootElement && - typeof transitionRootElement === 'object' && - 'current' in transitionRootElement - ) { + } else if (transitionRootElement && isRefObject(transitionRootElement)) { (transitionRootElement as React.MutableRefObject).current = el; } }} diff --git a/src/flashbar/utils.ts b/src/flashbar/utils.ts index efbc35aa22..e9b7ee8f62 100644 --- a/src/flashbar/utils.ts +++ b/src/flashbar/utils.ts @@ -1,5 +1,7 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 +import React from 'react'; + import { IconProps } from '../icon/interfaces'; import { FlashbarProps } from './interfaces'; @@ -13,6 +15,16 @@ export interface StackableItem extends FlashbarProps.MessageDefinition { collapsedIndex?: number; } +export function isStackableItem(item: StackableItem | FlashbarProps.MessageDefinition): item is StackableItem { + const key: keyof StackableItem = 'expandedIndex'; + return key in item; +} + +export function isRefObject(ref: unknown): ref is React.RefObject { + const key: keyof React.RefObject = 'current'; + return ref !== null && typeof ref === 'object' && key in ref; +} + const typesToColors: Record = { error: 'red', info: 'blue', diff --git a/src/mixed-line-bar-chart/utils.ts b/src/mixed-line-bar-chart/utils.ts index 0baa16f70c..3f7216c0c5 100644 --- a/src/mixed-line-bar-chart/utils.ts +++ b/src/mixed-line-bar-chart/utils.ts @@ -100,13 +100,15 @@ export const getKeyValue = (key: ChartDataTypes) => (key instanceof Date ? key.g export function isYThreshold( series: MixedLineBarChartProps.ChartSeries ): series is MixedLineBarChartProps.YThresholdSeries { - return series.type === 'threshold' && 'y' in series; + const key: keyof MixedLineBarChartProps.YThresholdSeries = 'y'; + return series.type === 'threshold' && key in series; } export function isXThreshold( series: MixedLineBarChartProps.ChartSeries ): series is MixedLineBarChartProps.XThresholdSeries { - return series.type === 'threshold' && 'x' in series; + const key: keyof MixedLineBarChartProps.XThresholdSeries = 'x'; + return series.type === 'threshold' && key in series; } export function isDataSeries( diff --git a/src/side-navigation/util.tsx b/src/side-navigation/util.tsx index 8f74d5543a..e6750833d0 100644 --- a/src/side-navigation/util.tsx +++ b/src/side-navigation/util.tsx @@ -50,14 +50,16 @@ export function checkDuplicateHrefs(items: ReadonlyArray