2

I've been having issues with this for a while. The code does run without issue, but I feel like I should figure out what TypeScript's linter is concerned about. Here's the simplest repro I can get, LocaleConfig is what gets the red highlight.

let Today = new Date();
let LocaleConfig = {dateStyle: "full"};
console.log(Today.toLocaleString([], LocaleConfig));

I've noticed that the linter has different errors depending on the target version. Here's the result from the default ES3.

 No overload matches this call.
   Overload 1 of 3, '(locales?: LocalesArgument, options?: DateTimeFormatOptions | undefined): string', gave the following error.
     Argument of type '{ dateStyle: string; }' is not assignable to parameter of type 'DateTimeFormatOptions'.
       Types of property 'dateStyle' are incompatible.
         Type 'string' is not assignable to type '\"full\" | \"long\" | \"medium\" | \"short\" | undefined'.
   Overload 2 of 3, '(locales?: string | string[] | undefined, options?: DateTimeFormatOptions | undefined): string', gave the following error.
     Argument of type '{ dateStyle: string; }' is not assignable to parameter of type 'DateTimeFormatOptions'.
{source: ts, code: 2769, severity: 8}

And here's from ES6. Error is on the same word.

Type '{ dateStyle: string; }' has no properties in common with type 'DateTimeFormatOptions'.
{source: ts, code: 2559, severity: 8}
BurntVoxel
  • 23
  • 4
  • You've got me stumped. Never had a matching string literal complain like that before. But declaring the type of the property fixes it for me. `let LocaleConfig: Intl.DateTimeFormatOptions = { dateStyle: 'full' };` – AlienWithPizza Sep 01 '22 at 03:49
  • 1
    It is because when you just assign a string it is typed as a string. Which is wider than a type made up of string literal. So you have to assert the type in some way. What I did above (probably the best in this case), or just assert the one property as const: `let localeConfig = { dateStyle: 'full' as const };` https://stackoverflow.com/a/37978675/9360315 – AlienWithPizza Sep 01 '22 at 04:01
  • Does this answer your question? [Typescript Type 'string' is not assignable to type](https://stackoverflow.com/questions/37978528/typescript-type-string-is-not-assignable-to-type) – AlienWithPizza Sep 01 '22 at 04:13
  • RE: declare property as `DateTimeFormatOptions`, that kinda just whack-a-moles the problem to the object literal on my end. VSCode, targeting either es3 or es6. "Type `{ dateStyle: string; }` is not assignable to type `DateTimeFormatOptions`. Object literal may only specify known properties, and `dateStyle` does not exist in type `DateTimeFormatOptions`." – BurntVoxel Sep 01 '22 at 06:14
  • I'd added `{dateStyle: 'full' as const}`, but that new error persists. I also tried that other question's solution of declaring a new type with the appropriate choices, `type Option = "full" | "long" | "medium" | "short"` and `{dateStyle: 'full' as Option}`, but I'm still stuck on the previous error. – BurntVoxel Sep 01 '22 at 06:14
  • Would it be appropriate to put the modified code as it currently stands in an edit of the original question? Tried pasting it here in comments but formatting is garbage but here goes anyway. ```type Option = "full" | "long" | "medium" | "short"; let Today = new Date(); let LocaleConfig: Intl.DateTimeFormatOptions = { dateStyle: 'full' as const }; console.log(Today.toLocaleString([], LocaleConfig));``` – BurntVoxel Sep 01 '22 at 06:15
  • The fix is as stated, to declare the type or assert the property as const. It will complain the same for `es3` and `es6`, that the property does not exist. `es6` was 2015, `dateStyle` wasn't even [drafted](https://tc39.es/proposal-intl-datetime-style/) untill 2020. If I paste that simple code into an empty vscode (with `as const`), it is not happy until `ES2020`. – AlienWithPizza Sep 01 '22 at 06:35
  • Ahhh. That's it. That dateStyle isn't available in anything older than 2020. Wanna write a formal answer so you can get the karma? Thanks @AlienWithPizza – BurntVoxel Sep 01 '22 at 07:30

1 Answers1

3

There are 2 separate issues here.

Type '{ dateStyle: string; }' has no properties in common with type 'DateTimeFormatOptions'.

This will happen when the target is set to anything pre ES2020, which is when the property was added to the spec. Find the draft here. ES6 == ES2015 and therefore it is missing when it is targeted.

Type 'string' is not assignable to type '"full" | "long" | "medium" | "short" | undefined'.

This is because of how string literals work. Without a type assertion, the dateStyle can only be determined to be of type string. Which could be changed at any time to be something not in the union of valid literals, which can therefore not reliably be assigned to it.

This can be resolved in multiple ways, but essentially you need to tell TS the dateStyle property is of some literal type or union of literal types that fits within the expected type.

We know the full expected type, so we can use that:
let LocaleConfig: Intl.DateTimeFormatOptions = { dateStyle: 'full' };
We could type assert as a literal in multiple ways:
let LocaleConfig = { dateStyle: 'full' as 'full' };
let LocaleConfig = { dateStyle: 'full' as 'full' | 'long' };
let LocaleConfig = { dateStyle: 'full' as Intl.DateTimeFormatOptions['dateStyle'] };
We could type assert as const in multiple ways:
let LocaleConfig = { dateStyle: 'full' as const };
let LocaleConfig = { dateStyle: 'full' } as const;

From TS handbook:

The as const suffix acts like const but for the type system, ensuring that all properties are assigned the literal type instead of a more general version like string or number.

Also found this answer around this problem.

AlienWithPizza
  • 386
  • 1
  • 8