reactjs 如果使用props上的spread操作符发送类型为any的对象,则TS中止检查

mgdq6dx1  于 2023-03-12  发布在  React
关注(0)|答案(1)|浏览(170)

查看以下代码示例

// Component.tsx file
import React from "react";

export const Component = ({ title, subTitle }: Props) => {
  return (
    <div>
      <h1>{title}</h1>
      <p>{subTitle}</p>
    </div>
  );
};

// Props declared here
type Props = {
  title: string;
  subTitle: string;
};
// App.tsx file
import { Component } from "./Component";
import "./styles.css";

export default function App() {
  const componentData: any = {
    title: "Hello",
    subTitle: "Hello from comp"
  };
  return (
    <div className="App">
      // TS does not show an error here even though randomProp is not described on Component's Props
      <Component {...componentData} randomProp="string" />
    </div>
  );
}

正如你所看到的,在App.tsx的第5行,componentData对象被输入为any,在App.tsx的第11行,我使用spread操作符将该对象作为另一个组件的 prop 发送。
如果我这样做,TS将中止检查发送的其余 prop ,如您所见,randomProp="string"不会抛出错误。
如果我从第5行的对象中删除:any类型,那么TS会在Component的属性中将randomProp="string"标记为不存在(我预计第5行的:any也会发生这种情况)。
以下示例不使用结构化,TS以这种方式显示错误

// App.tsx file
import { Component } from "./Component";
import "./styles.css";

export default function App() {
  const componentData: any = {
    title: "Hello",
    subTitle: "Hello from comp"
  };
  return (
    <div className="App">
      <Component 
       title={componentData.title}
       subTitle={componetData.subTitle}
       // TS shows an error here!
       randomProp="string"
      />
    </div>
  );
}

如需了解更多信息:
请看这个CodeSandbox:https://codesandbox.io/s/elated-worker-td2g0u?file=/src/App.tsx .
还有这个https://codesandbox.io/s/shy-monad-ygkloq?file=/src/App.tsx
这是一个有意的行为还是一个bug?这在下面是如何工作的?

x759pob2

x759pob21#

any类型显式地意味着关闭类型检查,它往往是“传染性的”或“传染性的”,因为将any与其他类型组合通常会导致any尽可能避免any
不过,如果你需要使用它,你应该了解更多细节,了解它如何与其他类型交互,以及它何时“接管”其他类型,何时不接管。

type Props = {
    title: string;
    subTitle: string;
};
const componentData: any = {
    title: "Hello",
    subTitle: "Hello from comp"
};

由于componendData的类型为any,编译器将允许您使用任何键对其进行索引,并且结果属性的类型也将为any

const title = componentData.title;
// const title: any
const subTitle = componentData.fsfsdjfiwjwi;
// const subTitle: any

如果您使用any类型的值作为属性值来创建对象文本,则只有那些属性将具有any类型;它不会“感染”整个对象:

const objLit = {
    title: title,
    subTitle: subTitle,
    randomProp: "abc"
};
/* const objLit: {
    title: any;
    subTitle: any;
    randomProp: string;
} */

因此,如果你试图给Props类型的变量赋值,编译器会欣然接受any属性(即使它无法验证它们,这正是any的意义所在),但它会捕获多余的属性,因为对象文本知道它们的存在:

const excess: Props = {
    title: componentData.title, // okay
    subTitle: componentData.fsfsdjfiwjwi,  // okay
    randomProp: "abc" // error!
};

现在让我们比较一下当你把一个any类型的值spread到一个对象常量中时会发生什么:

const objSpread = {
    ...componentData,
    randomProp: "abc"
};
// const objSpread: any

从概念上讲,扩展将导致类似于扩展对象类型与对象常量其余部分类型的交集(对于键已知的扩展,编译器将尝试更准确地表示被覆盖的属性,但对于泛型或其他未知键类型,它使用交集)。any与任何其他类型的交集就是any

type IntersectAny = any & { randomProp: string };
// type IntersectAny = any

所以objSpread的值现在是any类型,编译器已经完全忘记了randomProp,这意味着如果你给Props类型的值赋值,编译器不会看到任何多余的属性,因为any没有任何“多余”属性:

const noExcess: Props = {
    ...componentData,
    randomProp: "abc" // okay
};

所以,如果你把any扩展到一个对象字面量中,它会“感染”整个对象,变成any。但是如果你把any类型的属性单独添加到一个对象中,any类型将被限制在这些属性中。
Playground代码链接

相关问题