如何在打字稿中定义一个常量数组

Posted

技术标签:

【中文标题】如何在打字稿中定义一个常量数组【英文标题】:How to define a const array in typescript 【发布时间】:2019-01-23 00:59:21 【问题描述】:

我的代码:

const WEEKDAYS_SHORT: string[] = ['Dim', 'Lun', 'Mar', 'Mer', 'Jeu', 'Ven', 'Sam']

错误信息来自 TypeScript (3.0) 编译器:

TS2322:类型 'string[]' 不可分配给类型 '[string, string, string, string, string, string, string]'。 “字符串[]”类型中缺少属性“0”。

如果我把string[]改成ReadonlyArray<string>,那么错误信息就变成了:

TS2322:类型“ReadonlyArray”不可分配给类型“[string, string, string, string, string, string, string]”。 “ReadonlyArray”类型中缺少属性“0”。

我的 tsconfig.json:


  "compilerOptions": 
    "declaration": false,
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "lib": ["es6", "dom"],
    "module": "es6",
    "moduleResolution": "node",
    "sourceMap": true,
    "target": "es5",
    "jsx": "react",
    "strict": true
  ,
  "exclude": [
    "**/*.spec.ts",
    "node_modules",
    "vendor",
    "public"
  ],
  "compileOnSave": false

如何在 TypeScript 中定义只读数组?

【问题讨论】:

无法重现您的错误,即使使用 strict: true。 typescriptlang.org/play/… 嘿 @TitianCernicova-Dragomir 我添加了 tsconfig.json 【参考方案1】:

调试后发现不是TypeScript编译器的问题,是因为我使用了第三方组件调用DayPicker:

          <DayPicker
            onDayClick=this.handleDayClick
            selectedDays=posts.day
            months=MONTHS
            weekdaysShort=WEEKDAYS_SHORT
            firstDayOfWeek=1/>

道具weekdaysShort的类型不是string[],而是[string, string, string, string, string, string, string]

weekdaysShort?: [string, string, string, string, string, string, string];

所以 TS 编译说 string[][string, string, string, string, string, string, string] 不匹配。

最后,我只是将类型从string[] 更改为any 以避免出现此错误消息,当然我们也可以更改为[string, string, string, string, string, string, string](太长)。

【讨论】:

在这种情况下,我发现自己在进行类型定义,并使用该类型。即type WeekdaysShort = ['Dim', 'Lun', 'Mar', 'Mer', 'Jeu', 'Ven', 'Sam']。然后,const WEEKDAYS_SHORT: WeekdaysShort = ...【参考方案2】:

正如您指出的那样,问题是因为您尝试将字符串数组 (string[]) 分配给 7 字符串元组。虽然您使用any 的解决方案会起作用,但通常不建议使用any。拼出柔顺也不太理想,因为它太长了。

我们可以做的是创建一个辅助函数来创建一个元组类型。这个函数可以在任何你需要元组的地方重复使用:

function tupleArray<T extends any[]>(...v: T) 
    return v;

const WEEKDAYS_SHORT_INFFERED =  tupleArray('Dim', 'Lun', 'Mar', 'Mer', 'Jeu', 'Ven', 'Sam')  // INFFERED AS [string, string, string, string, string, string, string]
const WEEKDAYS_SHORT: [string, string, string, string, string, string, string] = WEEKDAYS_SHORT_INFFERED

【讨论】:

它有效!神奇!最后一行可以简化为const WEEKDAYS_SHORT = WEEKDAYS_SHORT_INFFERED @Spark.Bao 我的观点是对预期元组的变量的可分配性,类型只是为了证明这一点:)【参考方案3】:

我没有在TypeScript playground 上重现该问题。

但是,不管它是什么(字符串数组string[] 或7 字符串元组[string, string, string, string, string, string, string]),使用推断类型怎么样?

const WEEKDAYS_SHORT = ['Dim', 'Lun', 'Mar', 'Mer', 'Jeu', 'Ven', 'Sam'];
const sunday = 0;
const dayShortName = WEEKDAYS_SHORT[sunday]; // => 'Dim'

还是枚举?

enum WEEKDAYS_SHORT  'Dim', 'Lun', 'Mar', 'Mer', 'Jeu', 'Ven', 'Sam' 
const sunday = 0;
const dayShortName = WEEKDAYS_SHORT[sunday]; // => 'Dim'

IMO,在这种情况下,上述两个选项都比指定类型 any 更好。

【讨论】:

【参考方案4】:

断言你的数组声明as const;

const readonlyArray = [1, 2, 3] as const;

此外,访问readonly 数组的元素类型很简单:

type Element = typeof readonlyArray[number]` // 1 | 2 | 3

as const 断言适用于任何声明,包括对象和深度嵌套的数据结构。您甚至可以断言表达式as const。例如,如果值1 被推断为number,但您想提供数字文字类型,只需将其声明为1 as const。 该功能是在 TypeScript 3.4 中引入的。也可以参考the docs。

请注意,readonlyconst 数组通常称为元组(在 Python 中就是这种情况 - reference)。元组可以具有相同类型的元素或完全不同类型的元素。请注意,在上面的示例中,Element 类型没有被推断为number,而是作为数字文字的联合。这是可能的,因为元组的元素永远不会改变。推断最窄的可能类型是实用的。例如,您可以在需要number 的任何地方使用数字文字类型1,但不能在需要数字文字类型1 的地方使用number 类型。

两个元素的元组通常称为一对,可以表示为一个产品。虽然[number, string] 的乘积不是数字,但您仍然可以将它们相乘。因为stringnumber 本身都是无限大的,所以它们的产品类型也是无限大的。考虑[1 | 2 | 3, 'a' | 'b' | 'c'] 的更简单示例,它等于:

  [1, 'a']
| [1, 'b']
| [1, 'c']
| [2, 'a']
| [2, 'b']
| [2, 'c']
| [3, 'a']
| [3, 'b']
| [3, 'c']

进一步阅读:

readonly and const Product Types

【讨论】:

【参考方案5】:

虽然接受的答案是正确的,但它确实有一个缺点。

const readonlyArray = [1, 2, 3] as const;

let x = 4; // function parameter, etc.
// Argument of type 'number' is not assignable to parameter of type '1 | 2 | 3'.ts(2345)
// if (readonlyArray.includes(x)) 

另一种方法是使用readonly

const readonlyArray: readonly number[] = [1, 2, 3];

let x = 4;
if (readonlyArray.includes(x))  // OK

// Property 'push' does not exist on type 'readonly number[]'.ts(2339)
// readonlyArray.push(4);

【讨论】:

以上是关于如何在打字稿中定义一个常量数组的主要内容,如果未能解决你的问题,请参考以下文章

如何在打字稿中显示从数组中过滤的对象列表?

我如何遍历表单数组并将其值设置为打字稿中的另一个数组?

如何在打字稿中正确导入自定义类型

我将如何在打字稿中定义这个对象?

如何在打字稿中为 const 定义重载签名?

如何在打字稿中定义敲除绑定处理程序?