Skip to content

Inaccurate type for return value of Intl.RelativeTimeFormat.prototype.formatToParts #46245

Closed
@justingrant

Description

@justingrant

Bug Report

The types for Intl.RelativeTimeFormat.prototype.formatToParts in lib.e2020.intl.d.ts have a few issues.

  1. According to the spec (and MDN docs), either singular or plural names for units are accepted as input, but only singular names are output. The current types assume that plural names can also be output, which is incorrect. The relevant step in the spec is Step 5 in https://tc39.es/ecma402/#sec-PartitionRelativeTimePattern, where plural inputs are converted to singular before being used in the output.

  2. Also, there's a missed opportunity here to narrow the return type of this method, because for any input unit, the return unit will be the same unit, unless it's a plural in which case it's the plural unit converted to singular. (See https://tc39.es/ecma402/#sec-makepartslist.) So the return type could be a generic where unit in the output depends on unit in the input.

  3. Finally, there's another missed opportunity to narrow the return type using the type property, because according to https://tc39.es/ecma402/#sec-FormatRelativeTimeToParts and https://tc39.es/ecma402/#sec-makepartslist, if type === 'literal' then unit will be omitted, while if type is any other value, then unit will be present in the output.

I'd be happy to submit a PR to change these types. (If a PR would be welcome.)

Here's excerpts from the current relevant types in lib.e2020.intl.d.ts:

interface RelativeTimeFormatPart {
type: string;
value: string;
unit?: RelativeTimeFormatUnit;
}

formatToParts(
value: number,
unit: RelativeTimeFormatUnit,
): RelativeTimeFormatPart[];

type RelativeTimeFormatUnit =
| "year" | "years"
| "quarter" | "quarters"
| "month" | "months"
| "week" | "weeks"
| "day" | "days"
| "hour" | "hours"
| "minute" | "minutes"
| "second" | "seconds"
;

🔎 Search Terms

Intl
RelativeTimeFormat
formatToParts
plural
singular

🕗 Version & Regression Information

Introduced in #36084.

⏯ Playground Link

https://www.typescriptlang.org/play?target=99#code/MYewdgzgLgBATlAZjAvDMBTA7jAkmKAGwDoAlDQgQygEsA3DAFRoFsMAxEOF6gCgEoA3AFgAUKEiwADpQQRU8JMURceURiAAKsqBF4AWAEwAaGAHIWNMAFcoGCGaFiA9M5jkI1wlACqYGrA08lAAFhgwlMBQ1pSEMFAAnlLhcBjRcJgAJjDJcPFhMACiAMIAsgCCALT6AAyGMBDJwGKJye72Xr7+sGgARAkYsr0wAD4wvQCOMQgYcMNjvSzgofPjWBgYANarvZmUCcOj4yEg1nNHi1a2GDsQGBKZvSLi4NA5OhAA6gEhfgEKMjkyho3lmvCkqAAfDliNZuk4XpIYHCAgAudqebx-HrvOTfULYgDaNQAurDugBCZ6uGC0mAAPUZ9JcbkYSXCZnIVFoDGYbE43Go2LMMCC6BAsEoEAgNAA5mBKAAjQjhKAgeLs8weTrC4gsmBstpmfqDOAQXoisVgCURaVyhXK1Xq1oc7VY7pmYi8QwAZkMhn4YiAA

💻 Code

const rtf = new Intl.RelativeTimeFormat();
const parts = rtf.formatToParts(42, 'minutes');
// ResultUnit is the actual type returned per the ECMA-402 spec
type ResultUnit = "year" | "quarter" | "month" | "week" | "day"  | "hour" | "minute" | "second";
const partsWithUnit = parts.filter(p => p.unit);
const unit: ResultUnit = partsWithUnit[0].unit!;
//    ^^^^
// Type 'RelativeTimeFormatUnit' is not assignable to type 'ResultUnit'.
// Type '"years"' is not assignable to type 'ResultUnit'.(2322)

🙁 Actual behavior

TS error 2322

🙂 Expected behavior

No error because the unit of the return type is always singular.

Metadata

Metadata

Assignees

Labels

Fix AvailableA PR has been opened for this issueNeeds InvestigationThis issue needs a team member to investigate its status.RescheduledThis issue was previously scheduled to an earlier milestone

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions