0

I'm just getting my feet wet with TypeScript and Angular2. I'm working with an API that has a nested object structure. I would like my model to mirror the resource from the API. The model/resource, "Inquiry", as defined in TypeScript:

// inquiry.ts
export class Inquiry {
  name: {
    first: string,
    last: string
  };
  email: string;
  phone: string;
  question: string;
}

My form component is as such:

import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { InquireService } from './inquire.service';
import { Inquiry } from './inquiry';

@Component({
  selector: 'inquire-form',
  templateUrl: './inquire-form.component.html'
})
export class InquireFormComponent implements OnInit {
  inquiryForm: FormGroup;
  inquiry = new Inquiry;

  constructor(
    private formBuilder: FormBuilder,
    private inquireService: InquireService) {}

  ngOnInit(): void {
    this.buildForm();
  }

  buildForm(): void {
    this.inquiryForm = this.formBuilder.group({
      'firstName': [
        this.inquiry.name.first, [
          Validators.required,
          Validators.minLength(2),
          Validators.maxLength(50)
        ]
      ], ...
  }
}

The error I get when I hit my route is:

Error: Uncaught (in promise): TypeError: Cannot read property 'first' of undefined

When I log this.inquiry.name it is indeed undefined, but I'm not understanding why. What am I doing wrong?

6
  • 1
    You merely stated what the type signature of the name property is but haven't actually initialized it. Commented Sep 16, 2016 at 21:19
  • Hmm. Is it not initialized when I created the new Inquiry instance (above the constructor)? Commented Sep 16, 2016 at 23:11
  • As far as I understand, you just declared that an Inquiry object contains 4 properties, a name of type {first:string,last:string} and email, phone, and question of type string. None of the properties are explicitly initialized so they just have their default values (undefined). Commented Sep 16, 2016 at 23:15
  • I see. I'm new to Typescript. Should I / can I provide a default value of an empty string for those properties in the class definition? Or should I pass the default values in when I create the instance? Commented Sep 16, 2016 at 23:27
  • I'm not quite understanding why I have to provide a value for name but not the other properties. Commented Sep 16, 2016 at 23:48

1 Answer 1

2

The problem is that none of your properties on the Inquiry object are initialized so they're all the default value undefined. And since the one that matters name isn't initialized, attempts to access any of its properties (first or last) will fail.

Either set it in a constructor:

constructor() {
    this.name = {} as any; // too lazy to give first/last properties
}

or initialize it in the declaration:

class Inquiry {
    name = {} as {
        first: string,
        last: string
    };
    email: string;
    phone: string;
    question: string;
}

or if you want to explicitly keep the type in the declaration and not rely on type inference:

class Inquiry {
    name: {
        first: string,
        last: string
    } = {} as any;
    email: string;
    phone: string;
    question: string;
}
Sign up to request clarification or add additional context in comments.

4 Comments

Ah. The second example makes more sense. I'll give that a try as soon as I get back to my computer. Thank you very much for the help.
I implemented the second example and my formBuilder is working again. Thank you! Obviously I need to brush up on my TypeScript.
I wouldn't say this is necessarily a typescript thing, just general OOP. The difference here is that you're describing a pattern of an object and not giving it an explicit name. You declared a variable/property to an object with certain properties. But you still need to initialize that variable to some value as you would in say Java or C#.
Gotcha. It's been about 10 years since I've worked with C# :)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.