I want to declare arrays at the root of the module that are an array of keys that correspond to a model, but can be used in various parts of the app as unions.
const editableFields: (keyof Car)[] = ["name", "description"] as const
This generates the error:
The type 'readonly ["name", "description"]' is 'readonly' and cannot be assigned to the mutable type '(keyof Car)[]'.ts(4104)
I want to be able to create an union from this array, like "name" | "description" so that I can use it in various parts of the app, as well as somehow pass it to lodash pick and omit functions:
lodash.pick(carInstance, ...editableFields)
If I remove the (keyof Car)[] it all starts working:
const editableFields: readonly ["name", "description"]
But then I lose the type safety!
Would love to hear if anyone has set this up successfully. Thanks!
CodePudding user response:
When you use a const assertion, any array literal will get a readonly tuple type, which is a "read-only" array, but you were trying to assign that to a mutable or read-write array type, and the compiler doesn't see those as compatible.
One way to fix this is to just annotate the variable as readonly (keyof Car)[] instead of (keyof Car)[] so that the readonly conflict goes away:
const editableFields1: readonly (keyof Car)[] = ["name", "description"] as const
// const editableFields1: readonly (keyof Car)[]
Another approach is to use the new satisfies operator instead of a const assertion to tell the compiler that you want to be sure that the type of editableFields2 is assignable to (keyof Car)[]. This gives the compiler a context in which to interpret ["name", "description"], so that it doesn't get widened to string[]:
const editableFields2 = ["name", "description"] satisfies (keyof Car)[];
// const editableFields2: ("name" | "description")[]
If you made a mistake with one of your fields, then the compiler would output a warning (which is presumably what you mean by losing type safety when you don't annotate).
Finally, you could combine these approaches and use a const assertion with the right readonly type, but still use the satisfies operator so that the compiler checks the type without widening it:
const editableFields3 = ["name", "description"] as const satisfies readonly (keyof Car)[];
// const editableFields3: readonly ["name", "description"]
Here the type of editableFields3 is as specific as possible; the compiler knows that the first element is "name" and the type is "description".
