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

时间:2017-03-09 13:01:22

标签: typescript

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

我们也可以使用ReadonlyArray<string>声明只读数组,这意味着只读字符串数组。

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

6 个答案:

答案 0 :(得分:9)

由于[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)

答案 1 :(得分:7)

可接受的答案使数组突变方法不受影响,这可以通过以下方式导致不健全:

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

下面的代码解决了此问题,其中包括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

答案 2 :(得分:3)

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

TypeScript 3.4版本似乎可以为这一要求提供一个干净的解决方案:

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

语法如下:

let foo = ["text", 1] as const

let foo = <const> ["text", 1]
相应PR的

Microsoft.AspNet.Identity.EntityFramework。到目前为止,该功能应在typescript@next中可用。

答案 3 :(得分:2)

Readonly<[string, T]>不允许销毁。例如

const tuple: Readonly<[string, number]> = ["text", 4]

const [n, v] = tuple // error TS2488: Type 'Readonly<[string, number]>' must have a '[Symbol.iterator]()' method that returns an iterator.

因此,最好使用自定义界面

export interface Entry<T> {
    readonly [0]: string
    readonly [1]: T
    readonly [Symbol.iterator]: () => IterableIterator<string|T>
}

例如

const tuple: Entry<number> = ["text", 4]

const [name, value] = tuple // ok
const nameCheck: string = name
const valueCheck: number = value

答案 4 :(得分:1)

从v3.2.2开始,没有一种完美的方法来制作只读元组类型而不将其转换为看起来像数组但不是这样的对象。

TypeScript的首席架构师在将Readonly<T>与元组类型组合的话题上说了this

这是我想出的最好的解决方案:

type ReadonlyTuple<T extends any[]> = {
    readonly [P in Exclude<keyof T, keyof []>]: T[P]
} & Iterable<T[number]>

答案 5 :(得分:0)

从Typescript 3.4版开始,您可以在元组类型的前面加上关键字readonlysource)。

  

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

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