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"
.