繁体   English   中英

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

[英]How do I declare a read-only array tuple in TypeScript?

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

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

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

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

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

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

Typescript 3.4中的解决方案:Const上下文

对于TypeScript 3.4版本的这一要求,看起来会有一个干净的解决方案:

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

语法如下所示:

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

要么

let foo = <const> ["text", 1]

这是相应PR 的扩展信息 截至目前,该功能应该在typescript@next

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

从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 } 

从v3.2.2开始,没有完美的方法来制作只读元组类型而不将其转换为看起来像数组的对象,但事实并非如此。

打字本架构师所说对相结合的话题Readonly<T>用的元组类型。

这是我提出的最佳解决方案:

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM