通用Typescript函数无法识别对象

时间:2020-05-26 11:35:53

标签: typescript generics

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);

提供输出

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

P1是对象。我将对象传递给通用问候函数。此外,对象具有与问候函数需要的相同属性以显示输出。任何人都可以向新手TS爱好者解释为什么会变得“不确定”吗?

3 个答案:

答案 0 :(得分:4)

您收到undefined是因为this.namethis.age没有引用传递给函数的对象,而是引用了范围内的属性,这些属性显然是未定义的。

根据您提供的代码,没有理由为什么您的函数应该是通用的。您也没有将其用作通用函数。

首先,您应该为变量p1定义一个接口。您使用的是打字稿,可确保编译时键入的安全性。

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

现在您可以使用此界面初始化变量

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

这将强制p1类型为Person。它也是常数,这仅意味着您不能将其重新分配给p1,但是仍然可以更改其属性。

同样,您的函数不必是通用的,但您应该为参数提供类型,以便您的函数知道其处理的内容。

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

最后,您应该相应地命名参数,并提供一个返回类型,您可以使用typescript(即使它只是空的)也可以。

完整示例:

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);

答案 1 :(得分:1)

如果您无法执行@Mik所建议的另一种选择,则是类型警卫

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)();

答案 2 :(得分:1)

TypeScript只是将静态类型系统添加到JavaScript。在JavaScript中,您的greet()函数采用名为obj的参数,然后完全忽略它;相反,您使用的是this,其值不太可能对您有用。因此,您需要使用obj而不是this,因为其他答案正确指出了。

还指出,您似乎在这里不需要泛型。您只需将obj设为具体类型{name: string, age: number},它就会起作用:

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

greet()p1一起使用:

greet(p1); // okay

但是如果您用无法分配给所需类型的东西来调用它,就会抱怨:

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

,并且如果您由于excess property checking而传入具有多余属性的对象文字:

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

如果您真的要对该函数使用泛型,则可能应该在类型参数T上使用generic constraint,以便编译器知道它将具有字符串值name和数字值age属性:

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

因此T的实现内部仍不完全知道greet(),但是我们知道它必须是{name: string, age: number}的某种子类型,因此当我们阅读时没有编译器错误name的{​​{1}}和age属性。

调用obj之所以有效,是因为greet(p1)符合约束条件:

p1

而且,像以前一样,如果您传递的内容不符合约束条件,则会出现错误:

greet(p1); // okay

最后,如果您传递了比约束更具体的内容(例如额外属性),则不会出现错误,并且greet({ name: "the ageless one" }) // error, age is missing 将被推断为更具体的类型:

T

好的,希望能有所帮助;祝你好运!

Playground link to code