Search code examples
javascripttypescriptvue.jsvuejs3typing

TS & Vue3: Prevent using @ts-ignore for Type 'boolean' is not assignable to type 'never'


Wassup guys, can someone help me with an typing error regarding interfaces? This issue drives me crazy, I am not sure how to avoid this error without using @ts-ignore.

This is my function:

function proceed() {
  // @ts-ignore
  let mockOptions: MockOptions = {};
  Object.keys(models).forEach((modelKey) => {
    const _modelKey = modelKey as keyof MockOptions;
    const model = models[_modelKey];

    // @ts-ignore
    if (model.id.includes("not")) {
      mockOptions[_modelKey] = false;
    } else {
      mockOptions[_modelKey] = true;
    }
  });

  emit("proceed", mockOptions);
}

This is my interface:

export interface MockOptions {
  hasHotelComment: boolean;
  isInStornofrist: boolean;
  withDifferentBillingAddress: boolean;
  paymentOption: string;
}

What do I want to achieve? I do render RadioButtons that I receive from my Mock, to change data properties for show cases. Normally they are booleans, now I want to add string so I can set values from the selected Radio. However, I receive this error, because of the added 'paymentOption: string', when I switch to 'boolean', this error disappears:

TS2322: Type 'boolean' is not assignable to type 'never'.
const _modelKey: keyof MockOptions


Solution

  • You can define a new type of all object keys that hold boolean values using a mapped type:

    type BooleanKeys<T> = keyof {[K in keyof T as T[K] extends boolean ? K : never]: any}
    
    const _modelKey = modelKey as BooleanKeys<MockOptions>; // "hasHotelComment" | "isInStornofrist" | "withDifferentBillingAddress"
    

    Since you were asking, let's break it down by parts:

    type BooleanKeys<T> = keyof         // take only the keys
     {[                                 // from a mapped type
       K in keyof T                     // where keys are those of T 
       as                               // but cast them to
       T[K] extends boolean ? K : never // themself if their value is boolean, otherwise nothing (remove them)
      ]: any                            // we throw away the values, so anything is fine
     }
    

    The heavy lifting is done by the as, the documentation has more on that.