【问题标题】:Extended interface implementation as a function's argument作为函数参数的扩展接口实现
【发布时间】:2021-12-07 01:44:24
【问题描述】:

考虑以下代码:

interface IUserData {
    FirstName: string,
    LastName: string,
    Email: string,
    Password: string
}


interface IState extends IUserData {
    isSuccess: boolean
}

const state: IState = {
    FirstName: 'Jan',
    LastName: 'Kowalski',
    Email: 'email',
    Password: 'abc',
    isSuccess: true
} 

const testFunction = (userData: IUserData): void => {
    console.log(userData);
}

testFunction(state)

代码返回:

{
  FirstName: 'Jan',
  LastName: 'Kowalski',
  Email: 'email',
  Password: 'abc',
  isSuccess: true
}

问题是为什么函数甚至接受state 对象。我希望编译器至少会抛出参数类型不正确的错误。

对此进行测试,我想看看是否有可能向函数提供一个对象,该对象的属性是原始对象属性的子集,而不会将它们映射到新对象。

简而言之,所需的输出是:

{
  FirstName: 'Jan',
  LastName: 'Kowalski',
  Email: 'email',
  Password: 'abc',
}

【问题讨论】:

  • 为什么会抱怨?根据定义,实现 IState 的对象是实现 IUserData 的对象 - 无法通过该接口访问额外的属性,因此它们无论如何都无关紧要。并且 TypeScript 在运行时不存在,如果你想真正过滤掉一些属性,你必须为此编写 JavaScript

标签: typescript object types interface


【解决方案1】:

我希望编译器至少会抛出参数类型不正确的错误。

参数类型正确。 IStateIUserData子类型,因此在任何可以使用IUserData 的地方,都可以使用IState。这是对象类型层次结构的标准,不仅在 TypeScript 中,而且在 Java、C# 和许多其他中。 (TypeScript 对此一般规则有一个例外:当您将指定为 literal 的对象分配给 [let x: ABCType = {a: 1, b: 2, c: 3, d: 4}; 其中ABCType 只有abc ] 或者类似地,当在函数调用中使用对象字面量作为参数时,它会警告您“多余的属性”——不是因为从类型的角度来看它是不正确的,而是因为从实用的角度讲,它可能是一个编码错误。)

简而言之,所需的输出是:

{
 FirstName: 'Jan',
 LastName: 'Kowalski',
 Email: 'email',
 Password: 'abc',
}

您正在记录函数接收到的参数值。您的代码中没有任何内容可以修改或提取该对象的属性,因此该对象的所有属性(包括来自其子类型的属性)都存在。

TypeScript 不会对 values 做任何事情,这是运行时的事情,而 TypeScript 是编译时的事情(除了 enums 的小运行时存在)。如果您想编写一个删除多余属性的函数,则必须使用运行时代码显式执行此操作。 TypeScript 不会为你做这件事。 (遗憾的是,在从类型定义中获取属性名称的地方是不可能的,您必须采用另一种方式:从运行时实际存在的模型对象派生类型定义。)

这是一个这样做的例子(我不是推荐它,我只是向你展示如果你愿意,你可以如何做到这一点):

const sampleUserData = {
    FirstName: "Jan",
    LastName: "Kowalski",
    Email: "email",
    Password: "abc",
};
type IUserData = typeof sampleUserData;
const userDataKeys = new Set(Object.keys(sampleUserData));

const testFunction = (userData: IUserData): void => {
    // Filter out non-IUserData properties
    const filtered = Object.fromEntries(
        Object.entries(userData).filter(([key]) => userDataKeys.has(key))
    ) as IUserData;
    console.log(filtered);
};

然后这个:

interface IState extends IUserData {
    isSuccess: boolean
}

const state: IState = {
    FirstName: "Jan",
    LastName: "Kowalski",
    Email: "email",
    Password: "abc",
    isSuccess: true
};

testFunction(state);

日志:

{ "名字": "一月", “姓氏”:“科瓦尔斯基”, “电子邮件”:“电子邮件”, “密码”:“abc” }

Playground link

【讨论】:

  • 谢谢。这对我来说非常清楚。通过从对象推断类型,这是一个有趣的视角。很有启发性。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-05-12
  • 2021-08-07
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多