如何在 TypeScript 中声明只读数组元组?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/42696098/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How do I declare a read-only array tuple in TypeScript?
提问by Jonas Kello
We can declare a typed tuple in TypeScript, for example, with the type annotation [string, number]
. This means an array of 2 elements where the first element needs to be a string and the second a number.
我们可以在 TypeScript 中声明一个类型化的元组,例如,使用类型注释[string, number]
。这意味着一个包含 2 个元素的数组,其中第一个元素需要是一个字符串,第二个元素需要是一个数字。
We can also declare read-only arrays with ReadonlyArray<string>
which means a read-only array of strings.
我们还可以声明只读数组,ReadonlyArray<string>
这意味着字符串的只读数组。
Now I want to have a read-only tuple like in the first example, but I want it to be read-only like in the second example. How would I declare that?
现在我想要像第一个示例中那样的只读元组,但我希望它像第二个示例中那样是只读的。我该如何声明?
采纳答案by Mariusz Pawelski
From Typescript version 3.4 you can just prefix tuple type with readonly
keyword (source).
从 Typescript 3.4 版开始,您可以在元组类型前加上readonly
关键字(source)。
TypeScript 3.4 also introduces new support for
readonly
tuples. We can prefix any tuple type with thereadonly
keyword to make it areadonly
tuple, much like we now can with array shorthand syntax. As you might expect, unlike ordinary tuples whose slots could be written to,readonly
tuples only permit reading from those positions.function foo(pair: readonly [string, string]) { console.log(pair[0]); // okay pair[1] = "hello!"; // error }
TypeScript 3.4 还引入了对
readonly
元组的新支持。我们可以用readonly
关键字作为任何元组类型的前缀以使其成为readonly
元组,就像我们现在可以使用数组速记语法一样。正如您所料,与可以写入插槽的普通元组不同,readonly
元组只允许从这些位置读取。function foo(pair: readonly [string, string]) { console.log(pair[0]); // okay pair[1] = "hello!"; // error }
回答by Arg0n
Since the type [string, number]
already is an Array
, you can simply use:
由于类型[string, number]
已经是 an Array
,您可以简单地使用:
Readonly<[string, number]>
Readonly<[string, number]>
Example:
例子:
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)
回答by Jake Tunaley
The accepted answer leaves array mutation methods unaffected, which can cause unsoundness in the following way:
接受的答案使数组变异方法不受影响,这可能会通过以下方式导致不健全:
const tuple: Readonly<[number, string]> = [0, ''];
tuple.shift();
let a = tuple[0]; // a: number, but at runtime it will be a string
The code below fixes this issue, and includes Sergey Shandar's destructuring fix. You'll need to use --noImplicitAny
for it to work properly.
下面的代码修复了这个问题,包括 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
回答by ggradnig
Solution in Typescript 3.4: Const Contexts
Typescript 3.4 中的解决方案:Const Contexts
It looks like there will be a clean solution for this requirement coming with TypeScript 3.4 version:
看起来 TypeScript 3.4 版本会为这个要求提供一个干净的解决方案:
With so-called const contexts, the compiler can be told to treat an array or an object as immutable, meaning that their properties are read-only. This also allows the creation of literal tuple types with narrower type inference (i.e. your ["a", "b"]
can for the first time be of type ["a", "b"]
, not string[]
without specifiying the whole thing as a contextual type)
使用所谓的const 上下文,可以告诉编译器将数组或对象视为不可变的,这意味着它们的属性是只读的。这也允许创建具有更窄类型推断的文字元组类型(即您 ["a", "b"]
第一次可以是 type ["a", "b"]
,而不是string[]
没有将整个事物指定为上下文类型)
The syntax will look like this:
语法如下所示:
let foo = ["text", 1] as const
or
或者
let foo = <const> ["text", 1]
Here is the extended informationof the corresponding PR. As of now, the feature should be available in typescript@next
.
回答by Sergey Shandar
Readonly<[string, T]>
doesn't allow destruction. For example
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.
So, it's better to use a custom interface
所以,最好使用自定义界面
export interface Entry<T> {
readonly [0]: string
readonly [1]: T
readonly [Symbol.iterator]: () => IterableIterator<string|T>
}
For example
例如
const tuple: Entry<number> = ["text", 4]
const [name, value] = tuple // ok
const nameCheck: string = name
const valueCheck: number = value
回答by aleclarson
As of v3.2.2, there's no perfect way of making a readonly tuple type without converting it to an object that lookslike an array, but is not.
从 v3.2.2 开始,没有完美的方法可以制作只读元组类型而不将其转换为看起来像数组的对象,但实际上不是。
The lead architect of TypeScript has said thison the topic of combining Readonly<T>
with tuple types.
TypeScript 的首席架构师在与元组类型结合的主题上说过这句话Readonly<T>
。
Here is the best solution I've come up with:
这是我想出的最佳解决方案:
type ReadonlyTuple<T extends any[]> = {
readonly [P in Exclude<keyof T, keyof []>]: T[P]
} & Iterable<T[number]>