diff --git a/packages/vue-i18n-core/src/composer.ts b/packages/vue-i18n-core/src/composer.ts index 94ffbe4e7..9569e1c07 100644 --- a/packages/vue-i18n-core/src/composer.ts +++ b/packages/vue-i18n-core/src/composer.ts @@ -1113,13 +1113,19 @@ export interface ComposerDateTimeFormatting< * * @returns Formatted value */ - ( + < + Value extends number | Date | string = number, + Key extends string = string, + Return extends string | Intl.DateTimeFormatPart[] = + | string + | Intl.DateTimeFormatPart[] + >( value: Value, keyOrOptions: | Key | ResourceKeys | DateTimeOptions - ): string + ): Return /** * Datetime formatting * @@ -1134,14 +1140,20 @@ export interface ComposerDateTimeFormatting< * * @returns Formatted value */ - ( + < + Value extends number | Date | string = number, + Key extends string = string, + Return extends string | Intl.DateTimeFormatPart[] = + | string + | Intl.DateTimeFormatPart[] + >( value: Value, keyOrOptions: | Key | ResourceKeys | DateTimeOptions, locale: Locales - ): string + ): Return } /** @@ -2285,14 +2297,17 @@ export function createComposer(options: any = {}): any { } // d - function d(...args: unknown[]): string { - return wrapWithDeps<{}, string>( - context => Reflect.apply(datetime, null, [context, ...args]) as string, + function d(...args: unknown[]): string | Intl.DateTimeFormatPart[] { + return wrapWithDeps<{}, string | Intl.DateTimeFormatPart[]>( + context => + Reflect.apply(datetime, null, [context, ...args]) as + | string + | Intl.DateTimeFormatPart[], () => parseDateTimeArgs(...args), 'datetime format', root => Reflect.apply(root.d, root, [...args]), () => MISSING_RESOLVE_VALUE, - val => isString(val) + val => isString(val) || isArray(val) ) } diff --git a/packages/vue-i18n-core/test/composer.test-d.ts b/packages/vue-i18n-core/test/composer.test-d.ts index fd8b7eb9b..281a8fe6b 100644 --- a/packages/vue-i18n-core/test/composer.test-d.ts +++ b/packages/vue-i18n-core/test/composer.test-d.ts @@ -344,13 +344,23 @@ test('strict composer with direct options', () => { }>() expectTypeOf(strictDirectComposer.d(new Date())).toEqualTypeOf() expectTypeOf( - strictDirectComposer.d(new Date(), 'short', 'ja-JP') + strictDirectComposer.d(new Date(), 'short', 'ja-JP') ).toEqualTypeOf() expectTypeOf( strictDirectComposer.d(new Date(), { key: 'short', locale: 'zh' }) - ).toEqualTypeOf() + ).toEqualTypeOf() + expectTypeOf( + strictDirectComposer.d( + new Date(), + { + key: 'short', + locale: 'zh', + part: true + } + ) + ).toEqualTypeOf() expectTypeOf( - strictDirectComposer.d(new Date(), 'custom' as any) + strictDirectComposer.d(new Date(), 'custom' as any) ).toEqualTypeOf() expectTypeOf(strictDirectComposer.n(1)).toEqualTypeOf() expectTypeOf(strictDirectComposer.n(1, 'currency', 'zh')).toEqualTypeOf< diff --git a/packages/vue-i18n-core/test/composer.test.ts b/packages/vue-i18n-core/test/composer.test.ts index 5b589fca4..1515ad5c6 100644 --- a/packages/vue-i18n-core/test/composer.test.ts +++ b/packages/vue-i18n-core/test/composer.test.ts @@ -1047,6 +1047,83 @@ describe('d', () => { '12/20/2012, 07:00 AM' ) }) + + test('parts formatting', () => { + const { d } = createComposer({ + locale: 'en-US', + datetimeFormats: { + 'en-US': { + short: { + year: 'numeric', + month: '2-digit', + day: '2-digit', + hour: '2-digit', + minute: '2-digit', + timeZone: 'America/New_York' + } + }, + 'ja-JP': { + long: { + year: 'numeric', + month: '2-digit', + day: '2-digit', + hour: '2-digit', + minute: '2-digit', + second: '2-digit', + timeZone: 'America/New_York' + }, + short: { + year: 'numeric', + month: '2-digit', + day: '2-digit', + hour: '2-digit', + minute: '2-digit', + timeZone: 'Asia/Tokyo' + } + } + } + }) + const dt = new Date(2015, 12) + expect(d(dt, { key: 'short', part: true, year: '2-digit' })).toEqual([ + { type: 'month', value: '12' }, + { type: 'literal', value: '/' }, + { type: 'day', value: '31' }, + { type: 'literal', value: '/' }, + { type: 'year', value: '15' }, + { type: 'literal', value: ', ' }, + { type: 'hour', value: '07' }, + { type: 'literal', value: ':' }, + { type: 'minute', value: '00' }, + { type: 'literal', value: ' ' }, + { type: 'dayPeriod', value: 'PM' } + ]) + expect(d(dt, { key: 'short', locale: 'en-US', part: true })).toEqual([ + { type: 'month', value: '12' }, + { type: 'literal', value: '/' }, + { type: 'day', value: '31' }, + { type: 'literal', value: '/' }, + { type: 'year', value: '2015' }, + { type: 'literal', value: ', ' }, + { type: 'hour', value: '07' }, + { type: 'literal', value: ':' }, + { type: 'minute', value: '00' }, + { type: 'literal', value: ' ' }, + { type: 'dayPeriod', value: 'PM' } + ]) + expect(d(dt, { key: 'long', locale: 'ja-JP', part: true })).toEqual([ + { type: 'year', value: '2015' }, + { type: 'literal', value: '/' }, + { type: 'month', value: '12' }, + { type: 'literal', value: '/' }, + { type: 'day', value: '31' }, + { type: 'literal', value: ' ' }, + { type: 'hour', value: '19' }, + { type: 'literal', value: ':' }, + { type: 'minute', value: '00' }, + { type: 'literal', value: ':' }, + { type: 'second', value: '00' } + ]) + }) }) describe('n', () => { diff --git a/packages/vue-i18n/src/vue.d.ts b/packages/vue-i18n/src/vue.d.ts index b84917eb3..db1a29baa 100644 --- a/packages/vue-i18n/src/vue.d.ts +++ b/packages/vue-i18n/src/vue.d.ts @@ -671,7 +671,24 @@ declare module 'vue' { * * @returns formatted value */ - $d(value: number | Date, options: DateTimeOptions): string + $d< + value extends number | Date = number, + Key extends string = string, + Return extends string | Intl.DateTimeFormatPart[] = + | string + | Intl.DateTimeFormatPart[], + DefinedDateTimeFormat extends + RemovedIndexResources = RemovedIndexResources, + Keys = IsEmptyObject extends false + ? PickupFormatPathKeys<{ + [K in keyof DefinedDateTimeFormat]: DefinedDateTimeFormat[K] + }> + : never, + ResourceKeys extends Keys = IsNever extends false ? Keys : never + >( + value: number | Date, + options: DateTimeOptions + ): Return /** * Number formatting *