网站搜索

日期时间格式 - Intl.DateTimeFormat 在某些选项下导致 TypeScript 错误


我有以下代码,它运行得非常完美:

const option = {
    year: "numeric",
    month: "short",
    day: "numeric",
    weekday: 'long',
}

const myDate = new Intl.DateTimeFormat('de', {
    year: "numeric",
    month: "short",
    day: "numeric",
    weekday: 'long',
}).format(date)

const myDateTest2 = new Intl.DateTimeFormat('de', option as any).format(date)

这意味着如果我将对象直接传入构造函数,我就能得到一个日期。如果我先定义选项,然后使用 any,它也能工作。

但是这会抛出一个 TypeScript 错误:

const myDateTest = new Intl.DateTimeFormat('de', option).format(date)

如下:

Argument of type '{ year: string; month: string; day: string; weekday: string; }' is not assignable to parameter of type 'DateTimeFormatOptions'.
  Types of property 'weekday' are incompatible.
    Type 'string' is not assignable to type '"short" | "long" | "narrow"'.ts(2345)

这是为什么?

我尝试了多种 TS 配置,但没有一种能解决这个问题。

这是我现在所拥有的:

"compilerOptions": {
    "allowJs": false,
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "target": "ES2020",
    "sourceMap": true,
    "outDir": "./.tmp/build/",
    "moduleResolution": "node",
    "declaration": true,
    "lib": [
      "ES2020",
      "ES2020.Intl",
      "dom"
    ],
    "resolveJsonModule": true,
    "allowSyntheticDefaultImports": true
  },

TypeScript 版本是 "typescript": "^4.8.4",

答案#1

问题:变量的类型过于宽泛

你在这里遇到的情况是处理需要字符串字面量类型作为参数的函数时的常见问题。这并不是 Intl.DateTimeFormat 特有的。你可以阅读关于字面量推断的文档以获取更多信息。

让我们看看你的错误告诉了你什么:

类型为 '{ year: string; month: string; day: string; weekday: string; }' 的参数不能赋值给类型 'DateTimeFormatOptions' 的参数。

属性“weekday”的类型不兼容。

类型 'string' 不能赋值给类型 '"short" | "long" | "narrow"'.ts(2345)

你的 option.weekday 类型被推断为 string。这对于 Intl.DateTimeFormat 构造函数来说不够具体,该构造函数只允许三种可能的字符串字面量值:"short" | "long" | "narrow"。

value 你的 option 变量是没问题的。但 TypeScript 只看 类型,不看值。

当你这样写你的赋值语句时:

const option = {
    year: "numeric",
    month: "short",
    day: "numeric",
    weekday: "long",
}

TypeScript 推断类型为:

const option: {
    year: string;
    month: string;
    day: string;
    weekday: string;
}

我们需要以一种方式创建你的变量,使其类型更窄。

解决方案 1:声明类型

我们从你的错误中知道,你的 option 变量需要符合的类型是 Intl.DateTimeFormatOptions。在创建 option 变量时,我们可以使用该类型。

const option: Intl.DateTimeFormatOptions = {
    year: "numeric",
    month: "short",
    day: "numeric",
    weekday: "long",
}

TypeScript 将检查你的对象的每一个属性,以确保它们可分配给 Intl.DateTimeFormatOptions 类型的约束。

如果你提供一个无效的值,比如这样:

const option: Intl.DateTimeFormatOptions = {
    weekday: "numeric",
}

你会在 weekday 属性上看到红色下划线。出于这个原因,我更喜欢这种方法——它更容易准确地看到问题所在。对于 weekday: "numeric",你的错误信息将会是:

类型 '"numeric"' 不能赋值给类型 '"long" | "short" | "narrow" | undefined'

解决方案 2:as const

您可以在创建变量时使用 as const 语句来更改 TypeScript 推断类型的方式。这会告诉 TypeScript 将对象的所有属性视为它们的字面类型(在这里有解释)。

const option = {
    year: "numeric",
    month: "short",
    day: "numeric",
    weekday: "long",
} as const;

TypeScript 推断类型为:

const option: {
    readonly year: "numeric";
    readonly month: "short";
    readonly day: "numeric";
    readonly weekday: "long";
}

每个属性都有类似 "long" 的字符串字面量类型,而不仅仅是 string。

假设你的值是有效的,这些字面量类型将可以分配给更宽泛的 Intl.DateTimeFormatOptions 类型。例如,"long" 可以分配给 weekday 属性上的联合类型 '"short" | "long" | "narrow"'。