Search code examples
typescripttype-conversiontype-assertion

How to get a `readonly string[]` into a `string[]`?


I have the following data setup (not declaring as an enum because this is shared between TypeScript server code and non-TypeScript client code):

import { enumType } from 'nexus';

export const TYPE_ENUM = Object.freeze({
  H: 'H',
  S: 'S',
});
export const TYPES = Object.freeze(Object.values(TYPE_ENUM));

export const MyType = enumType({
  name: 'MyType',
  members: TYPES,
});

TypeScript is giving me the following warning regarding the members field:

Type '(readonly string[])[]' is not assignable to type '(string | EnumMemberInfo)[] | Record<string, string | number | boolean | object>'.
  Type '(readonly string[])[]' is not assignable to type '(string | EnumMemberInfo)[]'.
    Type 'readonly string[]' is not assignable to type 'string | EnumMemberInfo'.
      Type 'readonly string[]' is not assignable to type 'string'.ts(2322)
enumType.d.ts(29, 3): The expected type comes from property 'members' which is declared here on type 'EnumTypeConfig<"MyType">'

I understand the error. Can't put a readonly string[] into something expecting string[] because they are different types. My question, is what is the best way to overcome this?

I've seen that unwrapping and recreating the array works: members: [...TYPES] but that feels wrong.


Solution

  • You already have the answer: members: [...TYPES] because you need another mutable Array for function enumType.

    You may feel that something is wrong. But it is not, why? The function enumType expects a mutable Array, it means it can change its content, such as:

    function enumType(v: {name: string; members: string[]): string[] {
      v.members.push('JOKER');
      return v.members;
    }
    
    export const MyType = enumType({
      name: 'MyType',
      members: TYPES as unknown as string[],
    });
    // You would expect MyType is ['H', 'S', 'JOKER'] but you are having an error in strict mode as you were trying to add an element to a freezed array
    

    If you own the function enumType and you know that it does not change the array content, in this case, the function should accept a readonly string[]

    function enumType({v: {name: string; memebers: readonly string[]}) {
      ...
    }
    

    If you do not own the function: members: [...TYPES] is the best way.