Skip to content

Introduce Tuple helpers - #5807

Closed
mxdvl wants to merge 3 commits into
mainfrom
mxdvl/no-uncheked-indexed-access/tuples
Closed

mxdvl wants to merge 3 commits into
mainfrom
mxdvl/no-uncheked-indexed-access/tuples

Conversation

@mxdvl

@mxdvl mxdvl commented Aug 23, 2022 •

Copy link
Copy Markdown
Contributor

What does this change?

Note
A tuple is an array of fixed length. Most people are familiar with the words “couple“ and “triple“.
Read more on Wikipedia: Tuple

Introduce two tuple helpers which help narrowing types: nonEmpty and isTuple.

By checking an array is nonEmpty, you can ensure that there is at least a single element.
By checking that an array isTuple(…, n), you can ensure that there is precisely n elements in it.

Why?

These will throw errors when we have noUncheckedIndexedAccess in #5777.

When we try to access an element in an array, the TypeScript compiler will not warn us by default that the element might be undefined. There are two very common cases in our codebase, and these helpers cover them: checking that an array has a least one item, and checking that an array has a precise length.

These patterns are used a lot in the Fronts cards, forcing precise number of cards where we expect them, instead of failing softly like we do now.

@mxdvl
mxdvl force-pushed the mxdvl/no-uncheked-indexed-access/tuples branch from 195eb41 to e8aeb0d Compare August 23, 2022 11:45
@github-actions

github-actions Bot commented Aug 23, 2022 •

Copy link
Copy Markdown

⚡️ Lighthouse report for the changes in this PR

Lighthouse tested 2 URLs

⚠️ Budget exceeded for 2 of 10 audits.

Report for Article

tested url https://www.theguardian.com/commentisfree/2020/feb/08/hungary-now-for-the-new-right-what-venezuela-once-was-for-the-left

Category Status Expected Actual
First Contentful Paint ✅ 1500 1139
Largest Contentful Paint ✅ 3000 2195
Time to Interactive ✅ 3500 1900
Cumulative Layout Shift ⚠️ 0.002 0.004428
accessibility ✅ 0.97 1.000000

Report for Front

tested url https://www.theguardian.com/uk

Category Status Expected Actual
First Contentful Paint ✅ 1500 1206
Largest Contentful Paint ✅ 3000 2636
Time to Interactive ✅ 3500 3215
Cumulative Layout Shift ✅ 0.002 0.000000
accessibility ⚠️ 0.97 0.950000

@github-actions

github-actions Bot commented Aug 23, 2022 •

Copy link
Copy Markdown

Size Change: +982 B (0%)

Total Size: 1.64 MB

Filename Size Change
dotcom-rendering/dist/frontend.server.js 491 kB +742 B (0%)
ℹ️ View Unchanged
Filename Size Change
dotcom-rendering/dist/1382.legacy.********************.js 5.67 kB 0 B
dotcom-rendering/dist/1815.legacy.********************.js 30.9 kB 0 B
dotcom-rendering/dist/2344.legacy.********************.js 2.78 kB 0 B
dotcom-rendering/dist/259.legacy.********************.js 3.25 kB 0 B
dotcom-rendering/dist/286.********************.js 23.2 kB 0 B
dotcom-rendering/dist/286.legacy.********************.js 23.9 kB 0 B
dotcom-rendering/dist/2947.********************.js 2.74 kB 0 B
dotcom-rendering/dist/2947.legacy.********************.js 2.82 kB 0 B
dotcom-rendering/dist/3210.********************.js 8.58 kB 0 B
dotcom-rendering/dist/3210.legacy.********************.js 9.35 kB 0 B
dotcom-rendering/dist/3307.********************.js 3.12 kB 0 B
dotcom-rendering/dist/3584.********************.js 1.8 kB 0 B
dotcom-rendering/dist/3584.legacy.********************.js 1.8 kB 0 B
dotcom-rendering/dist/4019.********************.js 3.44 kB 0 B
dotcom-rendering/dist/4019.legacy.********************.js 3.48 kB 0 B
dotcom-rendering/dist/5058.********************.js 2.73 kB 0 B
dotcom-rendering/dist/5436.legacy.********************.js 2.68 kB 0 B
dotcom-rendering/dist/5553.********************.js 2.87 kB 0 B
dotcom-rendering/dist/5887.********************.js 5.94 kB 0 B
dotcom-rendering/dist/6101.********************.js 32.9 kB 0 B
dotcom-rendering/dist/6131.********************.js 4.29 kB 0 B
dotcom-rendering/dist/6131.legacy.********************.js 4.3 kB 0 B
dotcom-rendering/dist/6204.********************.js 5.54 kB 0 B
dotcom-rendering/dist/6400.********************.js 21.5 kB 0 B
dotcom-rendering/dist/6400.legacy.********************.js 21.5 kB 0 B
dotcom-rendering/dist/6959.legacy.********************.js 6.46 kB 0 B
dotcom-rendering/dist/7137.legacy.********************.js 6.4 kB 0 B
dotcom-rendering/dist/7148.********************.js 6.8 kB 0 B
dotcom-rendering/dist/7148.legacy.********************.js 7.31 kB 0 B
dotcom-rendering/dist/7576.********************.js 3.96 kB 0 B
dotcom-rendering/dist/7576.legacy.********************.js 5.38 kB 0 B
dotcom-rendering/dist/7800.********************.js 11.3 kB 0 B
dotcom-rendering/dist/7829.********************.js 2.8 kB 0 B
dotcom-rendering/dist/7829.legacy.********************.js 2.91 kB 0 B
dotcom-rendering/dist/7981.********************.js 4.32 kB 0 B
dotcom-rendering/dist/8129.legacy.********************.js 11.8 kB 0 B
dotcom-rendering/dist/8195.legacy.********************.js 2.98 kB 0 B
dotcom-rendering/dist/8414.********************.js 4.37 kB 0 B
dotcom-rendering/dist/8414.legacy.********************.js 4.44 kB 0 B
dotcom-rendering/dist/8471.legacy.********************.js 5.45 kB 0 B
dotcom-rendering/dist/8492.legacy.********************.js 4.41 kB 0 B
dotcom-rendering/dist/8612.********************.js 3.69 kB 0 B
dotcom-rendering/dist/8612.legacy.********************.js 4.18 kB 0 B
dotcom-rendering/dist/8808.********************.js 5.3 kB 0 B
dotcom-rendering/dist/9380.legacy.********************.js 2.83 kB 0 B
dotcom-rendering/dist/AlreadyVisited-importable.********************.js 4.52 kB 0 B
dotcom-rendering/dist/AlreadyVisited-importable.legacy.********************.js 4.52 kB 0 B
dotcom-rendering/dist/atomIframe.********************.js 757 B 0 B
dotcom-rendering/dist/atomIframe.legacy.********************.js 806 B 0 B
dotcom-rendering/dist/AudioAtomWrapper-importable.********************.js 459 B 0 B
dotcom-rendering/dist/AudioAtomWrapper-importable.legacy.********************.js 517 B 0 B
dotcom-rendering/dist/bootCmp.********************.js 9.65 kB 0 B
dotcom-rendering/dist/bootCmp.legacy.********************.js 13.3 kB 0 B
dotcom-rendering/dist/Branding-importable.********************.js 4.65 kB 0 B
dotcom-rendering/dist/Branding-importable.legacy.********************.js 4.65 kB 0 B
dotcom-rendering/dist/braze-web-sdk-core.********************.js 36.1 kB 0 B
dotcom-rendering/dist/braze-web-sdk-core.legacy.********************.js 36.1 kB 0 B
dotcom-rendering/dist/BrazeMessaging-importable.********************.js 9.44 kB 0 B
dotcom-rendering/dist/BrazeMessaging-importable.legacy.********************.js 10.1 kB 0 B
dotcom-rendering/dist/CalloutBlockComponent-importable.********************.js 4.26 kB 0 B
dotcom-rendering/dist/CalloutBlockComponent-importable.legacy.********************.js 4.55 kB 0 B
dotcom-rendering/dist/Carousel-importable.********************.js 11.7 kB 0 B
dotcom-rendering/dist/Carousel-importable.legacy.********************.js 12.2 kB 0 B
dotcom-rendering/dist/ChartAtomWrapper-importable.********************.js 261 B 0 B
dotcom-rendering/dist/ChartAtomWrapper-importable.legacy.********************.js 271 B 0 B
dotcom-rendering/dist/CommentCount-importable.********************.js 3.46 kB 0 B
dotcom-rendering/dist/CommentCount-importable.legacy.********************.js 3.56 kB 0 B
dotcom-rendering/dist/CommercialMetrics-importable.********************.js 6.47 kB 0 B
dotcom-rendering/dist/CommercialMetrics-importable.legacy.********************.js 810 B 0 B
dotcom-rendering/dist/CoreVitals-importable.********************.js 6.09 kB 0 B
dotcom-rendering/dist/CoreVitals-importable.legacy.********************.js 6.31 kB 0 B
dotcom-rendering/dist/debug.js 1.75 kB 0 B
dotcom-rendering/dist/DiscussionContainer-importable.********************.js 3.5 kB 0 B
dotcom-rendering/dist/DiscussionContainer-importable.legacy.********************.js 3.75 kB 0 B
dotcom-rendering/dist/DiscussionMeta-importable.********************.js 3.94 kB 0 B
dotcom-rendering/dist/DiscussionMeta-importable.legacy.********************.js 4.05 kB 0 B
dotcom-rendering/dist/DocumentBlockComponent-importable.********************.js 2.99 kB 0 B
dotcom-rendering/dist/DocumentBlockComponent-importable.legacy.********************.js 3.1 kB 0 B
dotcom-rendering/dist/dynamicImport.********************.js 2 kB 0 B
dotcom-rendering/dist/dynamicImport.legacy.********************.js 2.07 kB 0 B
dotcom-rendering/dist/EditionDropdown-importable.********************.js 4.1 kB 0 B
dotcom-rendering/dist/EditionDropdown-importable.legacy.********************.js 4.15 kB 0 B
dotcom-rendering/dist/EmbedBlockComponent-importable.********************.js 3.34 kB 0 B
dotcom-rendering/dist/EmbedBlockComponent-importable.legacy.********************.js 3.45 kB 0 B
dotcom-rendering/dist/embedIframe.********************.js 759 B 0 B
dotcom-rendering/dist/embedIframe.legacy.********************.js 810 B 0 B
dotcom-rendering/dist/EnhancePinnedPost-importable.********************.js 6.24 kB 0 B
dotcom-rendering/dist/EnhancePinnedPost-importable.legacy.********************.js 6.82 kB 0 B
dotcom-rendering/dist/FetchCommentCounts-importable.********************.js 1.73 kB 0 B
dotcom-rendering/dist/FetchCommentCounts-importable.legacy.********************.js 1.78 kB 0 B
dotcom-rendering/dist/FetchOnwardsData-importable.********************.js 2.22 kB 0 B
dotcom-rendering/dist/FetchOnwardsData-importable.legacy.********************.js 2.21 kB 0 B
dotcom-rendering/dist/FilterButton-importable.********************.js 2.64 kB 0 B
dotcom-rendering/dist/FilterButton-importable.legacy.********************.js 2.74 kB 0 B
dotcom-rendering/dist/FilterKeyEventsToggle-importable.********************.js 3.82 kB 0 B
dotcom-rendering/dist/FilterKeyEventsToggle-importable.legacy.********************.js 3.89 kB 0 B
dotcom-rendering/dist/FocusStyles-importable.********************.js 4.71 kB 0 B
dotcom-rendering/dist/FocusStyles-importable.legacy.********************.js 4.77 kB 0 B
dotcom-rendering/dist/ga.********************.js 2.83 kB 0 B
dotcom-rendering/dist/ga.legacy.********************.js 2.9 kB 0 B
dotcom-rendering/dist/GetCricketScoreboard-importable.********************.js 4.09 kB 0 B
dotcom-rendering/dist/GetCricketScoreboard-importable.legacy.********************.js 4.26 kB +68 B (+2%)
dotcom-rendering/dist/GetMatchNav-importable.********************.js 9.23 kB 0 B
dotcom-rendering/dist/GetMatchNav-importable.legacy.********************.js 9.49 kB 0 B
dotcom-rendering/dist/GetMatchStats-importable.********************.js 6.36 kB 0 B
dotcom-rendering/dist/GetMatchStats-importable.legacy.********************.js 7.14 kB 0 B
dotcom-rendering/dist/GetMatchTabs-importable.********************.js 3.08 kB 0 B
dotcom-rendering/dist/GetMatchTabs-importable.legacy.********************.js 3.24 kB 0 B
dotcom-rendering/dist/guardian-braze-components-banner.********************.js 11.3 kB 0 B
dotcom-rendering/dist/guardian-braze-components-banner.js 10.3 kB 0 B
dotcom-rendering/dist/guardian-braze-components-banner.legacy.********************.js 11.4 kB 0 B
dotcom-rendering/dist/guardian-braze-components-end-of-article.********************.js 9.28 kB 0 B
dotcom-rendering/dist/guardian-braze-components-end-of-article.js 9.75 kB 0 B
dotcom-rendering/dist/guardian-braze-components-end-of-article.legacy.********************.js 9.36 kB 0 B
dotcom-rendering/dist/GuideAtomWrapper-importable.********************.js 263 B 0 B
dotcom-rendering/dist/GuideAtomWrapper-importable.legacy.********************.js 273 B 0 B
dotcom-rendering/dist/initDiscussion.********************.js 11 kB 0 B
dotcom-rendering/dist/initDiscussion.legacy.********************.js 11.3 kB -2 B (0%)
dotcom-rendering/dist/InstagramBlockComponent-importable.********************.js 3 kB 0 B
dotcom-rendering/dist/InstagramBlockComponent-importable.legacy.********************.js 3.09 kB 0 B
dotcom-rendering/dist/InteractiveBlockComponent-importable.********************.js 4.26 kB 0 B
dotcom-rendering/dist/InteractiveBlockComponent-importable.legacy.********************.js 4.43 kB 0 B
dotcom-rendering/dist/islands.********************.js 11.2 kB -4 B (0%)
dotcom-rendering/dist/islands.legacy.********************.js 12 kB -4 B (0%)
dotcom-rendering/dist/KeyEventsCarousel-importable.********************.js 2.9 kB 0 B
dotcom-rendering/dist/KeyEventsCarousel-importable.legacy.********************.js 3.01 kB 0 B
dotcom-rendering/dist/KnowledgeQuizAtomWrapper-importable.********************.js 268 B 0 B
dotcom-rendering/dist/KnowledgeQuizAtomWrapper-importable.legacy.********************.js 280 B 0 B
dotcom-rendering/dist/LabsHeader-importable.********************.js 8.66 kB 0 B
dotcom-rendering/dist/LabsHeader-importable.legacy.********************.js 8.79 kB 0 B
dotcom-rendering/dist/Links-importable.********************.js 6.33 kB 0 B
dotcom-rendering/dist/Links-importable.legacy.********************.js 7.39 kB 0 B
dotcom-rendering/dist/LiveBlogEpic-importable.********************.js 6.21 kB 0 B
dotcom-rendering/dist/LiveBlogEpic-importable.legacy.********************.js 6.9 kB 0 B
dotcom-rendering/dist/Liveness-importable.********************.js 3.63 kB +51 B (+1%)
dotcom-rendering/dist/Liveness-importable.legacy.********************.js 3.7 kB +47 B (+1%)
dotcom-rendering/dist/MapEmbedBlockComponent-importable.********************.js 5.11 kB 0 B
dotcom-rendering/dist/MapEmbedBlockComponent-importable.legacy.********************.js 5.34 kB 0 B
dotcom-rendering/dist/MostViewedFooterData-importable.********************.js 7.59 kB 0 B
dotcom-rendering/dist/MostViewedFooterData-importable.legacy.********************.js 7.74 kB 0 B
dotcom-rendering/dist/MostViewedRightWrapper-importable.********************.js 6.98 kB 0 B
dotcom-rendering/dist/MostViewedRightWrapper-importable.legacy.********************.js 4.94 kB 0 B
dotcom-rendering/dist/newsletterEmbedIframe.********************.js 931 B 0 B
dotcom-rendering/dist/newsletterEmbedIframe.legacy.********************.js 975 B 0 B
dotcom-rendering/dist/OnwardsUpper-importable.********************.js 6.82 kB +41 B (+1%)
dotcom-rendering/dist/OnwardsUpper-importable.legacy.********************.js 7.03 kB 0 B
dotcom-rendering/dist/ophan.********************.js 7.24 kB 0 B
dotcom-rendering/dist/ophan.legacy.********************.js 7.46 kB 0 B
dotcom-rendering/dist/PersonalityQuizAtomWrapper-importable.********************.js 271 B 0 B
dotcom-rendering/dist/PersonalityQuizAtomWrapper-importable.legacy.********************.js 282 B 0 B
dotcom-rendering/dist/ProfileAtomWrapper-importable.********************.js 264 B 0 B
dotcom-rendering/dist/ProfileAtomWrapper-importable.legacy.********************.js 274 B 0 B
dotcom-rendering/dist/PulsingDot-importable.********************.js 1.66 kB 0 B
dotcom-rendering/dist/PulsingDot-importable.legacy.********************.js 1.76 kB 0 B
dotcom-rendering/dist/QandaAtomWrapper-importable.********************.js 262 B 0 B
dotcom-rendering/dist/QandaAtomWrapper-importable.legacy.********************.js 272 B 0 B
dotcom-rendering/dist/ReaderRevenueDev-importable.********************.js 4.58 kB 0 B
dotcom-rendering/dist/ReaderRevenueDev-importable.legacy.********************.js 4.6 kB 0 B
dotcom-rendering/dist/readerRevenueDevUtils.********************.js 3.87 kB 0 B
dotcom-rendering/dist/readerRevenueDevUtils.js 2.33 kB 0 B
dotcom-rendering/dist/readerRevenueDevUtils.legacy.********************.js 4.93 kB 0 B
dotcom-rendering/dist/ReaderRevenueLinks-importable.********************.js 5.87 kB 0 B
dotcom-rendering/dist/ReaderRevenueLinks-importable.legacy.********************.js 6.27 kB 0 B
dotcom-rendering/dist/relativeTime.********************.js 1.3 kB 0 B
dotcom-rendering/dist/relativeTime.legacy.********************.js 1.35 kB 0 B
dotcom-rendering/dist/RichLinkComponent-importable.********************.js 5.77 kB 0 B
dotcom-rendering/dist/RichLinkComponent-importable.legacy.********************.js 5.9 kB +43 B (+1%)
dotcom-rendering/dist/SecureSignupIframe-importable.********************.js 8.43 kB 0 B
dotcom-rendering/dist/SecureSignupIframe-importable.legacy.********************.js 8.76 kB 0 B
dotcom-rendering/dist/sentry.********************.js 684 B 0 B
dotcom-rendering/dist/sentry.legacy.********************.js 691 B 0 B
dotcom-rendering/dist/sentryLoader.********************.js 10.2 kB 0 B
dotcom-rendering/dist/sentryLoader.legacy.********************.js 14.1 kB 0 B
dotcom-rendering/dist/SetABTests-importable.********************.js 8.23 kB 0 B
dotcom-rendering/dist/SetABTests-importable.legacy.********************.js 2.74 kB 0 B
dotcom-rendering/dist/ShareCount-importable.********************.js 3.59 kB 0 B
dotcom-rendering/dist/ShareCount-importable.legacy.********************.js 3.7 kB 0 B
dotcom-rendering/dist/shimport.********************.js 2.78 kB 0 B
dotcom-rendering/dist/shimport.legacy.********************.js 2.79 kB 0 B
dotcom-rendering/dist/ShowHideContainers-importable.********************.js 726 B 0 B
dotcom-rendering/dist/ShowHideContainers-importable.legacy.********************.js 761 B 0 B
dotcom-rendering/dist/SignInGateMain.********************.js 4.48 kB 0 B
dotcom-rendering/dist/SignInGateMain.js 2.87 kB 0 B
dotcom-rendering/dist/SignInGateMain.legacy.********************.js 4.56 kB 0 B
dotcom-rendering/dist/SignInGateSelector-importable.********************.js 5.2 kB 0 B
dotcom-rendering/dist/SignInGateSelector-importable.legacy.********************.js 5.49 kB 0 B
dotcom-rendering/dist/SlotBodyEnd-importable.********************.js 2.29 kB 0 B
dotcom-rendering/dist/SlotBodyEnd-importable.legacy.********************.js 2.93 kB 0 B
dotcom-rendering/dist/SpotifyBlockComponent-importable.********************.js 5.04 kB 0 B
dotcom-rendering/dist/SpotifyBlockComponent-importable.legacy.********************.js 5.26 kB 0 B
dotcom-rendering/dist/StickyBottomBanner-importable.********************.js 6.85 kB 0 B
dotcom-rendering/dist/StickyBottomBanner-importable.legacy.********************.js 7.84 kB 0 B
dotcom-rendering/dist/SubNav-importable.********************.js 3.26 kB 0 B
dotcom-rendering/dist/SubNav-importable.legacy.********************.js 3.38 kB 0 B
dotcom-rendering/dist/TimelineAtomWrapper-importable.********************.js 262 B 0 B
dotcom-rendering/dist/TimelineAtomWrapper-importable.legacy.********************.js 273 B 0 B
dotcom-rendering/dist/TopicFilterBank-importable.********************.js 3.73 kB 0 B
dotcom-rendering/dist/TopicFilterBank-importable.legacy.********************.js 3.84 kB 0 B
dotcom-rendering/dist/TopRightAdSlot-importable.********************.js 1.99 kB 0 B
dotcom-rendering/dist/TopRightAdSlot-importable.legacy.********************.js 2.02 kB 0 B
dotcom-rendering/dist/TweetBlockComponent-importable.********************.js 1.8 kB 0 B
dotcom-rendering/dist/TweetBlockComponent-importable.legacy.********************.js 1.79 kB 0 B
dotcom-rendering/dist/UnsafeEmbedBlockComponent-importable.********************.js 3.01 kB 0 B
dotcom-rendering/dist/UnsafeEmbedBlockComponent-importable.legacy.********************.js 3.11 kB 0 B
dotcom-rendering/dist/VideoFacebookBlockComponent-importable.********************.js 5.13 kB 0 B
dotcom-rendering/dist/VideoFacebookBlockComponent-importable.legacy.********************.js 5.35 kB 0 B
dotcom-rendering/dist/VineBlockComponent-importable.********************.js 2.81 kB 0 B
dotcom-rendering/dist/VineBlockComponent-importable.legacy.********************.js 2.91 kB 0 B
dotcom-rendering/dist/YoutubeBlockComponent-importable.********************.js 5.7 kB 0 B
dotcom-rendering/dist/YoutubeBlockComponent-importable.legacy.********************.js 6 kB 0 B

compressed-size-action

@mxdvl
mxdvl force-pushed the mxdvl/no-uncheked-indexed-access/tuples branch from e8aeb0d to 4351d60 Compare August 23, 2022 12:09
@OllysCoding

Copy link
Copy Markdown
Contributor

What's the overall difference between this & something like myArray.length > n?

I understand there seems to be some typing differences, is T[] where you check the length is greater than 2 different than [T, T, T] for example?

@mxdvl mxdvl added the Health label Aug 23, 2022
import { nonEmpty } from '../web/lib/tuple';
import { enhanceImages } from './enhance-images';

if (!nonEmpty(images)) throw new Error('Empty images list');

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I find it exceedingly frustrating that we're having to add checks here to what is a static array declared in code. Putting aside whether this overall tuple pattern is necessary for the code, can we update our fixtures to use as const or find another way to ensure we don't need to check them?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as const is definitely an approach we could use, but it would require more refactoring that a single line, because you then need the consumers to receive readonly arrays. I have given it a go and it is non-trivial, which is why I opted for a single error that should “never occur”.

Even though these arrays are fixed in our code, it’s worth noting that a change in the way the fixtures are generated could mean that in the future we have more or less items, and our tests would start failing. If this happens, the developers will not get an explicit prompt of what failed. By adding this check, we ensure that such a failure will be as explicit as possible.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

html: '<p>Just some normal text</p>',
};

if (!(images[0] && images[1] && images[2] && images[3])) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be isTuple?

@mxdvl mxdvl Aug 23, 2022 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not quite, because we have more than 4 elements, we only care that the first four are defined.

You can omit the images[0], but that’s about it.

@mxdvl
mxdvl marked this pull request as ready for review August 23, 2022 12:27
@mxdvl
mxdvl requested a review from a team as a code owner August 23, 2022 12:27
@mxdvl

mxdvl commented Aug 23, 2022

Copy link
Copy Markdown
Contributor Author

What's the overall difference between this & something like myArray.length > n?

I understand there seems to be some typing differences, is T[] where you check the length is greater than 2 different than [T, T, T] for example?

The main difference is that we are creating type-guards for the compiler:

  • T[], if length === 0 can be narrowed to []
  • T[], if length === 1 can be narrowed to [T]
  • T[], if length >= 1 can be narrowed to [T, ...T[]]
  • T[], if length >= 3 can be narrowed to [T, T, T, ...T[]]
  • T[], if length < 3 can be narrowed to [] | [T] | [T, T]

@mxdvl
mxdvl force-pushed the mxdvl/no-uncheked-indexed-access/tuples branch 2 times, most recently from 3cc3246 to 1db7bbf Compare August 23, 2022 14:37
mxdvl added 3 commits August 23, 2022 16:09
These type-guards can prevent unchecked indexed accesses
This helps narrow many of the arrays in our codebase.
@mxdvl
mxdvl force-pushed the mxdvl/no-uncheked-indexed-access/tuples branch from 1db7bbf to cde8986 Compare August 23, 2022 15:23
@oliverlloyd

Copy link
Copy Markdown
Contributor

Are we making our code more complex by adding a rule that is guarding against a problem that we don't really have?

Both this PR using new helper functions, and the alternative with direct checks, add complexity to the code. I don't think we should be adding non idiomatic language features into DCR but the alternative type checks shown in the other PR make the code much harder to follow.

This complexity has a real cost, not just in terms of the time it takes to implement this rule but the much larger cost of the DCR repo being harder to work on moving forward. Every time anyone needs to access an array they will need to either learn these abstractions or jump through type hoops. In particular, the fronts container code feels like it would be especially impacted here which is something I think we should be cautious about.

But how often does Frontend send us arrays like [trail, trail, trail, undefined, trail]? And how many times have we had a problem with [key: string]: string;? Are the number of times we've had these problems worth the cost of guarding against them?

Perhaps we can look at solving any problems we've seen locally without a global rule? If we're using [key: string]: string; we could see about being more explicit or adding more checks around particular cases. And if we really are being sent undefined values insides arrays of data then, well, I am fairly sure we're not because Scala, but we could look to filter them out where this might happen.

@mxdvl

mxdvl commented Aug 24, 2022 •

Copy link
Copy Markdown
Contributor Author

Thanks for raising your concerns @oliverlloyd – I came to propose this change as I believe it solves some real business issues, so will try and articulate some of the benefits as answers to your points above. 👇

Are we making our code more complex by adding a rule that is guarding against a problem that we don't really have?

It is hard to say whether we have these problems currently, as this is mainly used for containers that are not used in production. Currently, the burden of safety is on the implementation of these components, and we are relying on array.length with the hope that no edge case has escaped our scrutiny.

I have found the Dynamic container PRs hard to review and would feel quite uneasy refactoring their logic. I found introducing tuples retraining the number of cards a component accepts both freeing and simpler, hence my proposal to introduce tuples more broadly.

This complexity has a real cost […] of the DCR repo being harder to work on moving forward.

This is an excellent point and I really was hoping that these abstractions were making things easier, not harder. With the array type, any specific length requirement has to be expressed in the JS logic—like we currently do. This means that the interface of our components is neither an accurate representation of what they truly need, nor is it preventing the logic from drifting away in the future.

In general, this means pushing the error cases to the edge of the application, and greatly reducing checks at the deepest levels. Instead of forcing components to handle any size of arrays when they can really only ever render, say, 4 items, we can use the compiler to enforce this all the way up the stack.

Every time anyone needs to access an array they will need to either learn these abstractions or jump through type hoops. In particular, the fronts container code feels like it would be especially impacted here which is something I think we should be cautious about.

Not all arrays need to be checked, only some specific cases where we actually expect a fixed length.

[…] how many times have we had a problem with [key: string]: string;? Are the number of times we've had these problems worth the cost of guarding against them?

Looking at logs of 500, I could only find one example:

I don’t know how you balance errors v safety, but I like to know that these kinds of bugs can be avoided.

Perhaps we can look at solving any problems we've seen locally without a global rule? If we're using [key: string]: string; we could see about being more explicit or adding more checks around particular cases. And if we really are being sent undefined values insides arrays of data then, well, I am fairly sure we're not because Scala, but we could look to filter them out where this might happen.

I think the main benefit of TypeScript is preventing runtime errors and this is a real that can happen, so I would be enclined to turn it on?

@mxdvl

mxdvl commented Aug 25, 2022

Copy link
Copy Markdown
Contributor Author

Moving this back to draft, as we need to come up with better recommendations around introducing new TypeScript types.

@mxdvl
mxdvl marked this pull request as draft August 25, 2022 08:58
@mxdvl mxdvl closed this Sep 21, 2022
@mxdvl
mxdvl deleted the mxdvl/no-uncheked-indexed-access/tuples branch November 15, 2022 14:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants