From 9ea117c656386e085b54e080a2c2dce1e892f615 Mon Sep 17 00:00:00 2001 From: Jan Amann Date: Mon, 18 Nov 2024 17:35:02 +0100 Subject: [PATCH] fix: Disallow string dates, improve autocomplete (#1557) --- .../use-intl/src/core/TranslationValues.tsx | 4 +--- .../src/core/createTranslator.test.tsx | 22 +++++++++++++++++++ .../use-intl/src/core/createTranslator.tsx | 11 +++++----- packages/use-intl/src/core/index.tsx | 4 +--- 4 files changed, 30 insertions(+), 11 deletions(-) diff --git a/packages/use-intl/src/core/TranslationValues.tsx b/packages/use-intl/src/core/TranslationValues.tsx index 3c290a897..43e9bd1f7 100644 --- a/packages/use-intl/src/core/TranslationValues.tsx +++ b/packages/use-intl/src/core/TranslationValues.tsx @@ -1,9 +1,7 @@ import type {ReactNode} from 'react'; -// These type names are shown to consumers in autocomplete export type ICUArg = string | number | boolean | Date; -export type ICUNumber = number; -export type ICUDate = Date | number | string; +// ^ Keep this in sync with `ICUArgument` in `createTranslator.tsx` export type TranslationValues = Record; diff --git a/packages/use-intl/src/core/createTranslator.test.tsx b/packages/use-intl/src/core/createTranslator.test.tsx index 1271fbbc6..218c1f236 100644 --- a/packages/use-intl/src/core/createTranslator.test.tsx +++ b/packages/use-intl/src/core/createTranslator.test.tsx @@ -244,6 +244,28 @@ describe('type safety', () => { t('msg', obj); }); + it('validates numbers', () => { + const t = translateMessage('Percentage: {value, number, percent}'); + t('msg', {value: 1.5}); + + // eslint-disable-next-line @typescript-eslint/no-unused-expressions + () => { + // @ts-expect-error + t('msg', {value: 'test'}); + }; + }); + + it('validates dates', () => { + const t = translateMessage('Date: {date, date, full}'); + t('msg', {date: new Date('2024-07-09T07:06:03.320Z')}); + + // eslint-disable-next-line @typescript-eslint/no-unused-expressions + () => { + // @ts-expect-error + t('msg', {date: '2024-07-09T07:06:03.320Z'}); + }; + }); + it('validates cardinal plurals', () => { const t = translateMessage( 'You have {count, plural, =0 {no followers yet} =1 {one follower} other {# followers}}.' diff --git a/packages/use-intl/src/core/createTranslator.tsx b/packages/use-intl/src/core/createTranslator.tsx index 05fe5620a..e535fbf7b 100644 --- a/packages/use-intl/src/core/createTranslator.tsx +++ b/packages/use-intl/src/core/createTranslator.tsx @@ -12,8 +12,6 @@ import type { } from './MessageKeys.tsx'; import type { ICUArg, - ICUDate, - ICUNumber, MarkupTagsFunction, RichTagsFunction } from './TranslationValues.tsx'; @@ -33,9 +31,12 @@ type ICUArgsWithTags< > = ICUArgs< MessageString, { - ICUArgument: ICUArg; - ICUNumberArgument: ICUNumber; - ICUDateArgument: ICUDate; + // Provide types inline instead of an alias so the + // consumer can see the types instead of the alias + ICUArgument: string | number | boolean | Date; + // ^ Keep this in sync with `ICUArg` in `TranslationValues.tsx` + ICUNumberArgument: number; + ICUDateArgument: Date | number; } > & ([TagsFn] extends [never] ? {} : ICUTags); diff --git a/packages/use-intl/src/core/index.tsx b/packages/use-intl/src/core/index.tsx index 97627d2c5..775ab964c 100644 --- a/packages/use-intl/src/core/index.tsx +++ b/packages/use-intl/src/core/index.tsx @@ -1,11 +1,9 @@ export type {default as AbstractIntlMessages} from './AbstractIntlMessages.tsx'; export type { TranslationValues, + ICUArg, RichTranslationValues, MarkupTranslationValues, - ICUArg, - ICUNumber, - ICUDate, RichTagsFunction, MarkupTagsFunction } from './TranslationValues.tsx';