1
let p1 = {name: "steve", age: 5};

function   greet <T> (obj: T) {
  console.log(`Hello, my name is ${this.name}, I am ${this.age} years old.`);
}

greet(p1);

Gives output

Hello, my name is undefined, I am undefined years old.

P1 is object. I am passing Object to Generic Greet function. Also, object has same properties that greet function needs to display output. Can anyone explain why am getting "undefined" to novice TS enthusiast, please?

5
  • I think you made a typo. It should be obj.name, obj.age instead of this. Otherwise you have to bind function to object greet.bind(p1) Commented May 26, 2020 at 11:40
  • @JózefPodlecki this will not work because obj is of type T, it doesn't know about name and age. Commented May 26, 2020 at 11:46
  • @Jozef, When I use obj instead of this, I get following compiler errors 1) Property 'name' does not exist on type 'T' 2) Property 'age' does not exist on type 'T'. But get expected output (when TS converts code into JS) When I remove obj and use this instead, I DON'T get any compilation error. Commented May 26, 2020 at 11:46
  • I'll use different type of object and see how Generics work in TS. I am beginner in TS and learning on my own. I have definite done some mistakes here but can't figure it out. So I hope some senior/expert can show me my mistakes and correct me. so, what's the point of generic in this case --- just learning and clearing my understanding :) Commented May 26, 2020 at 11:58
  • Using generics in this case doesn't make much sense. If obj is generic, the function greet doesn't know it has the property name and age. That's why you get compilation errors when you use this.name and this.age (even if it will run correctly) Commented May 26, 2020 at 12:05

3 Answers 3

3

You are receiving undefined because this.name and this.age do not reference the object that you passed to the function, instead they reference properties in scope, which are apparently undefined.

Based on the code you provided, there is no reason why your function should be generic. You're not using it as a generic function either.

First, you should define an interface for your variable p1. You're using typescript, which allows for compile time type safety.

interface Person {
name: string;
age: number;
}

Now you can use this interface to initialize your variable

const p1: Person = {name: "steve", age: 5};

This will enforce that p1 is of type Person. It is also constant, which only means that you can't reassign to p1, you can still change it's properties though.

Again, your function does not need to be generic, but you should provide a type to the parameter, so your function knows what it's dealing with.

function greet(a_oPerson: Person): void {
  console.log(`Hello, my name is ${a_oPerson.name}, I am ${a_oPerson.age} years old.`);
}

Lastly, you should name your parameters accordingly, and also provide a return type, which you can do with typescript (even if it's just void).

Full example:

interface Person {
name: string;
age: number;
}

const p1: Person = {name: "steve", age: 5};

function greet(a_oPerson: Person): void {

  console.log(`Hello, my name is ${a_oPerson.name}, I am ${a_oPerson.age} years old.`);
}

greet(p1);
Sign up to request clarification or add additional context in comments.

Comments

1

TypeScript just adds a static type system to JavaScript. In JavaScript, your greet() function takes a parameter named obj and then completely ignores it; instead you're using this, whose value is unlikely to be anything useful to you. So you need to use obj instead of this, as the other answers correctly point out.

It was also pointed out that you don't seem to need generics here. You can just make obj of the concrete type {name: string, age: number} and it will work:

function greet(obj: { name: string, age: number }) {
  console.log(`Hello, my name is ${obj.name}, I am ${obj.age} years old.`);
}

And greet() will work with p1:

greet(p1); // okay

but complain if you call it with something that is not assignable to the required type:

greet({ name: "the ageless one" }) // error, age is missing

and also if you pass in an object literal with excess properties, due to excess property checking:

greet({ name: "one-eyed jack", age: 45, eyes: 1 }) // excess property warning,
// eyes not expected

If you really want to use generics for this function, you should probably use a generic constraint on your type parameter T, so that the compiler knows that it will have a string-valued name and a numeric-valued age property:

function greet<T extends { name: string, age: number }>(obj: T) {
  console.log(`Hello, my name is ${obj.name}, I am ${obj.age} years old.`);
}

So T is still not known exactly inside the implementation of greet(), but we know it has to be some subtype of {name: string, age: number}, and so now there's no compiler error when we read the name and age properties of obj.

Calling greet(p1) works because p1 meets the constraint:

greet(p1); // okay

And, as before, if you pass in something that doesn't meet the constraint you get an error:

greet({ name: "the ageless one" }) // error, age is missing

Finally, if you pass in something more specific than the constraint such as an extra property, there will be no error, and T will be inferred as the more specific type:

greet({ name: "one-eyed jack", age: 45, eyes: 1 }) // okay,
// T is inferred as {name: string, age: number, eyes: number}

Okay, hope that helps; good luck!

Playground link to code

1 Comment

Hi, @jcalz, It really helped me a lot and cleared my understanding. I really thank you for taking your time out and explaining me about my mistakes. I had understanding that this(keyword)==object in JS. I was thinking that if I pass object as parameter into function, this (keyword) will eventually replace with actual object and I can use properties of that object inside the function , hence I was using this (keyword). Just like we use this(keyword) in Call method. Would you mind telling me bit more about this (keyword) briefly and how/when I can use that in function, please?
1

if you can't do what @Mik suggests another option is type guard

interface CertainObjectType {
    name: string;
    age: number;
}

let p1 = {name: "steve", age: 5};

function isCertainObjectType(obj: any): obj is CertainObjectType {
    return obj.name !== undefined;
}

function greet<T> (obj: T) {
    if(isCertainObjectType(obj)) {
        console.log(`Hello, my name is ${obj.name}, I am ${obj.age} years old.`);
    }
}

greet(p1);

function greet1<T>(this: T) {

    if(isCertainObjectType(this)) {
        console.log(`Hello, my name is ${this.name}, I am ${this.age} years old.`);
    }
}

greet1.bind(this)();

Comments

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.