Home > Mobile >  Typescript does not detect properties which should not be there
Typescript does not detect properties which should not be there

Time:12-30

I have a function which is formatting data, and the "values" within this data are well-defined. However, when mapping these values, TS is not picking up on new properties which are invalid. The problem is best explained with code (see the comments in the code):

Here is the playround

type TextBoxValue = {
  text: string;
}
type TextBox = {
  values: TextBoxValue[];
}
type Result = {
  result: string | object;
}

const getTheValue = (): Result => ({ result: 'This comes from a child process.' });

const formatTextBox = (textbox: TextBox): TextBox => {
  const { result } = getTheValue();
  const originalValue = textbox.values[0];

  if (typeof result === 'object') {
    const arr = Array.isArray(result) ? result : [result];
    return {
      ...textbox,
      values: arr.map(res => {
        return { 
          foo: 'bar', // why does this not throw an error
          text: res.text || originalValue.text,
        };
      })
    }
  }

  return {
    ...textbox,
    values: [{ 
      ...originalValue, 
      foo: 'bar', // but this one does?
      text: String(result) 
    }]
  }
}

I know I can use satisfies and other techniques to get the error I want, but I'm curious why TS does not pick up on the invalid property with the code above.

CodePudding user response:

Your map is returning an object of type { foo: string, text: string } which sufficiently overlaps with TextBoxValue to keep the compiler happy (but it will treat it as an array of TextBoxValues).

That foo value will not show in any of the type hints and attempting to reach it with (for example) const v = result.values[0].foo will generate a Property 'foo' does not exist on type 'TextBoxValue' error.

If you use a type declaration in the map it will show the error

values: arr.map(res => {
  // declare type
  const textBoxValue: TextBoxValue  =  { 
  // Error: Type '{ foo: string; text: any; }' is not assignable to type 'TextBoxValue'.
    foo: 'bar', 
    text: res.text || originalValue.text,
  };
  return textBoxValue;
})

Similarly, if you use a type assertion in the final return, you're telling the compiler "trust me ... it's a TextBoxValue" and as long as there is enough overlap to satisfy TextBoxValue, it will stop complaining.

return {
    ...textbox,
    values: [{ 
      ...originalValue, 
      foo: 'bar', // No more whining
      text: String(result)
    // assert that I'm a `TextBoxValue` 
    } as TextBoxValue]
  }
  • Related