Hur deklarerar jag en skrivskyddad array-tupel i TypeScript?
Vi kan deklarera en typad tuple i TypeScript, till exempel med typannotationen [string, number]. Detta betyder en array med 2 element där det första elementet måste vara en sträng och det andra ett nummer.
Vi kan också deklarera skrivskyddade arrayer med ReadonlyArray<string> vilket betyder en skrivskyddad array av strängar.
Nu vill jag ha en skrivskyddad tuple som i det första exemplet, men jag vill att den ska vara skrivskyddad som i det andra exemplet. Hur skulle jag deklarera det?
Svar #1
Lösning för TypeScript 3.4+: Const Assertion
Med const-assertioner kan kompilatorn instrueras att behandla en array eller ett objekt som oföränderligt, vilket betyder att dess egenskaper är skrivskyddade. Detta möjliggör också skapandet av litterala tuplar med smalare typinferens (d.v.s. din ["a", "b"] kan vara av typen ["a", "b"], istället för string[] utan att specificera hela som en kontextuell typ)
Syntaxen:
const foo = ["text", 1] as const // or
const foo = <const> ["text", 1]
// typeof foo:
readonly ["text", 1]
Det kan också användas för objektliteraler:
const myObj = {
foo: 1,
bar: ["a", "b"],
baz: true,
} as const
// typeof myObj:
{ readonly foo: 1, readonly bar: readonly ["a", "b"], readonly baz: true }
Här är den utökade informationen om motsvarande PR.
Svar #2
Eftersom typen [string, number] redan är en Array, kan du helt enkelt använda:
Readonly<[string, number]>Exempel :
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)
Svar #3
Från och med Typescript version 3.4 kan du bara prefixa tuppeltypen med nyckelordet readonly (källa).
TypeScript 3.4 introducerar också nytt stöd för
readonlytuples. Vi kan prefixa vilken tupletyp som helst medreadonly-nyckelordet för att göra det till enreadonlytuple, på samma sätt som vi nu kan med arraykortsyntax. Som du kanske förväntar dig, till skillnad från vanliga tuples vars platser kunde skrivas till, tillåterreadonlytuples endast läsning från dessa positioner.function foo(pair: readonly [string, string]) { console.log(pair[0]); // okay pair[1] = "hello!"; // error }
Svar #4
Det accepterade svaret lämnar array-mutermetoder opåverkade, vilket kan orsaka osäkerhet på följande sätt:
const tuple: Readonly<[number, string]> = [0, ''];
tuple.shift();
let a = tuple[0]; // a: number, but at runtime it will be a string
Koden nedan åtgärdar detta problem och inkluderar Sergey Shandars destruktureringsfix. Du måste använda --noImplicitAny för att det ska fungera korrekt.
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
