【问题标题】:TypeScript: functions without interfaceTypeScript:没有接口的函数
【发布时间】:2021-12-07 12:52:30
【问题描述】:

是否允许在没有任何界面的情况下使用功能道具?

我有一个带有属性的函数:

from - HTML 元素

to - HTML 元素

coords - 数组 [2, 2]

export const adjustElements = ({ from, to, coords }) => {
    let to_rect = to.getBoundingClientRect(),
        width = to_rect.width * ((coords[0] - 1) * 0.5),
        height = to_rect.height * ((coords[1] - 1) * 0.5);

    from.style.top = (to_rect.top + height) + "px";
    from.style.left = (to_rect.left + width) + "px";
}

但是当我运行代码时出现以下错误:

Binding element 'from' implicitly has an 'any' type.

Binding element 'to' implicitly has an 'any' type.

Binding element 'coords' implicitly has an 'any' type.

之所以会这样,是因为我没有给函数添加任何接口,但问题是我有很多这样的函数,但是参数不同,所以给大家写一个新的接口会很疯狂......有没有一种方法可以让 typescript 不再需要每个函数的接口?

【问题讨论】:

  • 您在这里指的是什么“接口”?您具体看到了什么错误?请查看How to Ask 的指南以及minimal reproducible example 的构成要素。它应该是足够的信息,以便其他人可以为自己展示您所看到的任何内容,因此如果您看到“很多错误”,我们可以知道它们是什么。解决错误的正确方法取决于所讨论的错误。我可以告诉你使用类型断言any//@ts-ignore,但我也可以告诉你从火警中取出电池而不是检查火警。
  • 是的,你当然可以留下没有指定类型的东西。但为什么?这就是 TypeScript 的全部意义所在。如果您目前正在转换为 TypeScript,那么您可能希望放宽该规则。
  • 但是每个函数的写接口有点奇怪。
  • 我仍然看不到任何地方都需要接口。您可以使用匿名类型,例如this;那是你要的吗?如果是这样,我可以写一个答案。
  • 如果document.querySelector(...) 返回null 会发生什么?你怎么知道不会?编译器应该怎么知道呢?我很高兴为最初的问题写下答案,但我们无法将问题的范围扩展到您在第一件事解决后遇到的所有问题,否则问题将成为个人的技术支持会议,这不是堆栈溢出的重点。如果您有后续问题,您应该单独提问,并遵循How to Ask 的指导方针并制作minimal reproducible example(“不起作用”是不够的)。 See

标签: javascript typescript interface


【解决方案1】:

由于编译器无法推断从函数参数解构的fromtocoords 变量的类型,它回退到推断the any type,这是故意不安全的,您可以这样做几乎没有进一步错误的任何东西。假设您启用了the --strict suite of compiler features(并且您可能应该启用)或者甚至只是启用了the --noImplicitAny compiler option,这种自动回退到any 将生成一个警告,以便您完全意识到正在发生类型安全的损失。


正确的做法是annotate the function parameter 告诉编译器它将是什么类型。如果您想要使用any 并且只是使警告静音,您可以明确地这样做:

export const adjustElements = ({ from, to, coords }: any) => {
  let to_rect = to.getBoundingCleintRect(),
    width = to_rect.width * ((coords[0] - 1) * 0.5),
    height = to_rect.heigth * ((coords[1] - 1) * 0.5);

  from.style.top = (to_rect.top + height) + "px";
  from.style.left = (to_rect.left + width) + "px";
} // no error

但这允许对adjustElements 进行各种疯狂的调用,您可能希望看到编译器错误:

adjustElements("oopsie"); // no error

以及允许疯狂的实现;考虑到在上面的代码中,我完全拼错了getBoundingClientRectto_rect.height,但编译器未能捕捉到任何一个错误,因为toto_rect 都是any 类型,编译器不再担心你的问题了正在处理这些。

所以你不想这样做。


相反,您应该考虑函数参数必须是什么类型。您已经这样做了:fromto 属性应该是 HTMLElementcoords 属性应该是长度为 2 的 tuple,其中每个元素都是 number

你可以为这个对象类型命名interface,但是如果你只打算使用一次,你可能不想麻烦给它一个明确的名字。在这种情况下,您可以使用 匿名 object type:

export const adjustElements = ({ from, to, coords }:
  { from: HTMLElement, to: HTMLElement, coords: [number, number] }
) => {
  let to_rect = to.getBoundingClientRect(),
    width = to_rect.width * ((coords[0] - 1) * 0.5),
    height = to_rect.height * ((coords[1] - 1) * 0.5);

  from.style.top = (to_rect.top + height) + "px";
  from.style.left = (to_rect.left + width) + "px";
}

现在这可以工作了,编译器会捕捉到拼写错误,它还会捕捉到对adjustElements() 的虚假调用:

adjustElements("oopsie"); // error
// Argument of type 'string' is not assignable to parameter of 
// type '{ from: HTMLElement; to: HTMLElement; coords: [number, number]; }

这是一个错误,因为你给了它一个 string 它期望你的匿名对象类型,而 string 是不合适的。


请注意,编译器也将以下内容视为虚假调用:

adjustElements({
  from: document.querySelector(".wheel-pointer"),
//~~~~ <-- Type 'HTMLElement | null' is not assignable to type 'HTMLElement'
  to: document.querySelector(".wheel"),
//~~ <-- Type 'HTMLElement | null' is not assignable to type 'HTMLElement'
  coords: [2, 1]
}) // error!

错误告诉您确切的问题:fromto 属性很可能是null,因为document.querySelector() 有时会返回null。编译器不知道文档中存在"wheel-pointer""wheel" 类的任何内容,因此这是一个错误。如果您想拨打这样的电话,您需要检查这些不是null,或者断言它们不是。检查可能如下所示(使用truthiness narrowing):

const wheelPointerElement = document.querySelector(".wheel-pointer");
if (!wheelPointerElement) throw new Error("OH NO");
const wheelElement = document.querySelector(".wheel");
if (!wheelElement) throw new Error("WHY ME");

adjustElements({
  from: wheelPointerElement,
  to: wheelElement,
  coords: [2, 1]
}) // okay

虽然断言可能如下所示:(使用the non-null assertion operator !):

adjustElements({
  from: document.querySelector(".wheel-pointer")!,
  to: document.querySelector(".wheel")!,
  coords: [2, 1]
}) // okay

解释检查和断言之间的区别、各自的优缺点以及执行它们的不同方法与原始问题相去甚远。只要知道这些错误情况首先是 TypeScript 的一部分。在您的原始 JavaScript 代码中,您必须等到运行时才能在 adjustElements(null, null, [2, 1]) 上看到错误。 TypeScript 会提前警告您在运行时可能会发生类似的事情,并让您有机会解决它。

Playground link to code

【讨论】:

    【解决方案2】:

    编辑: 正如 jcalz 在评论中指出的那样,我给出的答案不是有效的 TypeScript。我错过了你传递一个包裹在 {} 中的参数,而不是传递三个单独的参数。


    你需要告诉 TypeScript 你的函数参数的类型是什么。如果你不这样做,并且 TypeScript 无法以某种方式判断(例如,如果它们有默认值),那么它将推断它们的类型为 any。但是默认编译器设置会阻止implicit any 类型。

    你不需要使用接口,你可以这样做:

    export const adjustElements = ({ from: HTMLElement, to: HTMLElement, coords: [number, number] }) => {
    

    【讨论】:

    • 啊,好点子。在复制/粘贴代码进行编辑时,我没有发现它是一个包含在 {} 中的单个参数,而不是三个单独的参数。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-12-20
    • 2019-09-27
    • 2018-03-21
    • 2020-09-13
    • 2021-09-26
    • 2015-02-15
    • 1970-01-01
    相关资源
    最近更新 更多