typescripttypes

How can I create a TypeScript type for an array that contains exactly one Social field followed by any number of Fields in any order?


I'm working with TypeScript and I have the following types:

type Social = "facebook" | "twitter" | "instagram";
type Fields = "firstName" | "surname";
type PossibleFields = [Social, ...Fields[]];

I want to create a type PossibleFields where the array contains exactly one field from Social and any number of fields from Fields[], but the order doesn't matter.

I know that this solution may not be the most optimal, but I am curious if this type definition is even possible. I want to ensure that:

Is there a way to express this in TypeScript?

Thank you for your help!

I attempted to define the type PossibleFields as follows:

type Social = "facebook" | "twitter" | "instagram";
type Fields = "firstName" | "surname";
type PossibleFields = [Social, ...Fields[]];

Solution

  • Given the small number of the unions' members you could calc all possible tuples (around 30). As a bonus you will have IntelliSense for your typing:

    Playground

    type Social = "facebook" | "twitter" | "instagram";
    type Fields = "firstName" | "surname";
    
    type Insert<
      T extends unknown[],
      U
    > = 
    T extends [infer F,...infer L]
      ? [F,U,...L] | [F,...Insert<L,U> ] 
      : [U]
    
    type PermutationsOfTuple<
      T extends unknown[],
      R extends unknown[] = T
    > = 
    T extends [infer F,...infer L]?
      PermutationsOfTuple<L,Insert<R,F> | [F,...R] >
      :R
    
    type OptionalTuples<T, K=T> =
        [T] extends [never]
          ? []
          : K extends K
            ? [] | [K] | [K, ...OptionalTuples<Exclude<T, K>>]
            : never
    
    type PossibleFields = {[K in Social]: [K] | PermutationsOfTuple<[K], OptionalTuples<Fields>>}[Social];
    
    
    const fields: PossibleFields = ['facebook', 'firstName', 'surname']; // ok
    const fields2: PossibleFields = ['surname', 'facebook']; // ok
    const fields3: PossibleFields = ['surname']; // Social missing
    const fields4: PossibleFields = ['facebook', 'instagram']; // Social doubled