如何在TypeScript中声明只读数组元组?

Jon*_*llo 14 typescript

我们可以在TypeScript中声明一个类型化的元组,例如,使用类型注释[string, number].这意味着一个包含2个元素的数组,其中第一个元素需要是一个字符串,第二个元素需要一个数字.

我们还可以声明只读数组,ReadonlyArray<string>这意味着只读数组的字符串.

现在我想在第一个例子中有一个只读元组,但我希望它像第二个例子中那样是只读的.我该如何申报?

Arg*_*g0n 11

由于该类型[string, number]已经是Array,您可以简单地使用:

Readonly<[string, number]>

例:

let tuple: Readonly<[string, number]> = ['text', 3, 4, 'another text'];

tuple[0] = 'new text'; //Error (Readonly)

let string1: string = tuple[0]; //OK!
let string2: string = tuple[1]; //Error (Type number)
let number1: number = tuple[0]; //Error (Type string)
let number2: number = tuple[1]; //OK!
let number3: number = tuple[2]; //Error (Type any)
Run Code Online (Sandbox Code Playgroud)

  • 对我不起作用,当我尝试修改Readonly数组时,我没有出现类型错误.这是我正在尝试的代码,TypeScript不会抱怨:`const a:Readonly <number []> = [1,2,3]; a [1] = 4`.我的应用中没有错误!我在TS`3.3.0-dev.20181208`上.也不在操场上(写作时)!参见:http://www.typescriptlang.org/play/#src=const%20a%3A%20Readonly%3Cnumber%5B%5D%3E%20%3D%20%5B1%2C2%2C3%5D%0Aa%5B1 %5D%20%3D%204%0A (2认同)
  • 第一行产生打字稿错误:“类型'[字符串,数字,数字,字符串]”不能分配给类型“只读[字符串,数字]”。源有 4 个元素,但目标仅允许 2 个。(2322)` (2认同)

Rub*_*ley 8

接受的答案会使阵列变异方法不受影响,这可能会导致以下方式造成不健全:

const tuple: Readonly<[number, string]> = [0, ''];
tuple.shift();
let a = tuple[0]; // a: number, but at runtime it will be a string
Run Code Online (Sandbox Code Playgroud)

下面的代码解决了这个问题,包括Sergey Shandar的解构修复.您需要使用--noImplicitAny它才能正常工作.

type ArrayItems<T extends ReadonlyArray<any>> = T extends ReadonlyArray<infer TItems> ? TItems : never;

type ExcludeProperties<TObj, TKeys extends string | number | Symbol> = Pick<TObj, Exclude<keyof TObj, TKeys>>;

type ArrayMutationKeys = Exclude<keyof any[], keyof ReadonlyArray<any>> | number;

type ReadonlyTuple<T extends any[]> = Readonly<ExcludeProperties<T, ArrayMutationKeys>> & {
    readonly [Symbol.iterator]: () => IterableIterator<ArrayItems<T>>;
};

const tuple: ReadonlyTuple<[number, string]> = [0, ''];
let a = tuple[0]; // a: number
let b = tuple[1]; // b: string
let c = tuple[2]; // Error when using --noImplicitAny
tuple[0] = 1; // Error
let [d, e] = tuple; // d: number, e: string
let [f, g, h] = tuple; // Error
Run Code Online (Sandbox Code Playgroud)


Mar*_*ski 8

从 Typescript 3.4 版开始,您可以在元组类型前加上readonly关键字(source)。

TypeScript 3.4 还引入了对readonly元组的新支持。我们可以使用readonly关键字为任何元组类型添加前缀以使其成为readonly元组,就像我们现在可以使用数组速记语法一样。正如您所料,与可以写入插槽的普通元组不同,readonly元组只允许从这些位置读取。

function foo(pair: readonly [string, string]) {
    console.log(pair[0]);   // okay
    pair[1] = "hello!";     // error
}
Run Code Online (Sandbox Code Playgroud)


ggr*_*nig 5

打字稿3.4中的解决方案:常量上下文

看来TypeScript 3.4版本会提供针对此要求的干净解决方案:

使用所谓的const上下文,可以告诉编译器将数组或对象视为不可变的,这意味着它们的属性是只读的。这还允许创建具有更窄类型推断的文字元组类型(即,您 ["a", "b"]可以第一次使用type ["a", "b"],而不是string[]将整个事物指定为上下文类型)

语法如下所示:

let foo = ["text", 1] as const
Run Code Online (Sandbox Code Playgroud)

要么

let foo = <const> ["text", 1]
Run Code Online (Sandbox Code Playgroud)

这是相应PR 的扩展信息。到目前为止,该功能应该在中可用typescript@next。