2

I am using the airbnb typescript style guideline and I have defined a type like this:

export interface Question {
  id: number;
  type: 'text' | 'multipleOption' | 'checkBox';
  question: string;
  answer: string | Array<string>;
}

A variable that fits this interface is created and I need to perform a forEach on the answer to check some conditions, but I get a linting error saying that

Property 'forEach' does not exist on type 'string | string[]'.
  Property 'forEach' does not exist on type 'string'

I would like to perform a check that enables me to perform this foreach without modifying the guideline configuration or changing the type in the interface Question definition.

I have tried:

if (question.answer.constructor === Array) {
    question.answer.forEach(...)
}

if (Array.isArray(question.answer)) {
    question.answer.forEach(...)
}
if (typeof question.answer !== 'string') {
    question.answer.forEach(...)
}

But none of the above removes the lining error.

  • `const answers = >question.answer; answers.forEach(...)` - let me know if that works and I'll post it as an answer. cheers – Kinglish May 24 '21 at 23:54
  • 1
    The latter two look good. – Bergi May 24 '21 at 23:58
  • 1
    [I cannot reproduce](https://www.typescriptlang.org/play?#code/KYDwDg9gTgLgBASwHY2FAZgQwMbDgRQFdgBnGBCJOAbwCg5EATALjiUIFsAjNAbnrgwAnmGCsA5KhAxxcAD5xxHQgBtyYFcADyYcpVkLx2ABbBsAawBCEEOP4MAjsTIUkrMlGQBze3ExISAHc0dxhPJC95OABBKChMIQAeD28APn4AX1psSjI4J1I9KgBeGgy-EgJnIv5aBHQ4AAoCl0oAOn8gtDacgLDCbBhoOGLRmLiEgEoaAUdq1w6A4Kg29GgAURxjRt6SCE02lQgvSdos+qbY+KE2hBIrhOb59s7lyem6BjnChdfutagmxMO1y+2Ah2Op3ODUawlEEAaLSKiy6UDgAEIxuIUhFxB9ZvlnkgUctVhstiCAmCISczkA), all three approaches work fine. – Bergi May 25 '21 at 00:00
  • 2
    I don't know much about the airbnb guide, but that interface is ill-designed. You might be better off with a tagged union of three question interfaces with respective `answer` types. – georg May 25 '21 at 00:01
  • Are you sure this is a linter warning? It sounds like a typescript error message. – Bergi May 25 '21 at 00:01

1 Answers1

4
if (Array.isArray(question.answer)) {
    (question.answer as Array<string>).forEach(...)
}

also you can do this to have a uniform code

    let answers : Array<string> = [];
    if (Array.isArray(question.answer)) {
        answers = [...question.answer];
    } else if (!!question.answer){
        answers = [question.answer];
    }

    answers.forEach(answer => ....)
Reza
  • 18,865
  • 13
  • 88
  • 163
  • This cast is not necessary. `Array.isArray` already acts as a type guard. – Bergi May 25 '21 at 01:22
  • 1
    Btw, no reason for cloning the array. Just write `const answers = Array.isArray(question.answer) ? question.answer : [question.answer];` – Bergi May 25 '21 at 01:23