Gabriel Savian
Gabriel Savian

Reputation: 224

Property 'title' does not exist on type '{}'. TS2339

I'm switching from JavaScript to TypeScript, everything was going okay until I entered in this situation. I set my interface in this way:

interface StreamCreateProps {
  [title: string]: any 
};

My code is:

const validate = (formValues: any) => {
  const errors = {};

  if(!formValues.title) {
    errors.title = 'You must enter a title';
  };

  if(!formValues.description) {
    errors.description = 'You must enter a description';
  };

  return errors;
};

How can I type the errors.title and errors.description correctly? The full error is: Property 'title' does not exist on type '{}'.

Upvotes: 1

Views: 2123

Answers (1)

Connor Low
Connor Low

Reputation: 7226

Use the Record utility type:

const errors: Record<string, string> = {};
errors.foo = 'okay';

When you declare a variable without specifying the type, you cause the TypeScript Compiler to infer its type for you. The compiler will choose a reasonably narrow type. A few examples:

const foo = false // false
foo = true // ERROR
// `foo` is constant, so TS infers `false`, the only possible value. 

let foo = false // boolean
foo = true // OK
foo = 100 // ERROR
// Using `let` or `var` will result in `foo` being reassignable, 
//   so it could technically be anything; 
//   however, TS assumes a narrower type based on its initial value. 

// `foo` is constant, but it's properties are not, 
//   so, like with the previous row, `bar` is assignable to any boolean. 
const foo = { bar: false } // { bar: boolean; } 
foo.bar = true // OK
foo.bar = 100 // ERROR

// a `const` litteral declaration: 
//   foo and its subproperties should retain the narrowest possible type.
const foo = { bar: false } as const // { readonly bar: false; }
foo.bar = false // ERROR

So applied to your situation:

const foo = {} // {}
foo.bar = 'baz' // ERROR: Property 'bar' does not exist on type '{}'

foo is too narrow to accept additional properties. Record<string, string> is less restrictive: it ensures you retain something like { [key: string]: string }. If you need to work with non-string values, you could widen it further: Record<string, string | number> or Record<string, any>.

Examples

Upvotes: 1

Related Questions