If an object typed as any is sent using the spread operator on props, TS aborts checking

huangapple go评论72阅读模式
英文:

If an object typed as any is sent using the spread operator on props, TS aborts checking

问题

以下是翻译的代码部分:

// Component.tsx 文件
import React from "react";

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

// 在这里声明 Props
type Props = {
  title: string;
  subTitle: string;
};
// App.tsx 文件
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 不会在这里显示错误,即使 randomProp 未在 Component 的 Props 中描述 */}
      <Component {...componentData} randomProp="string" />
    </div>
  );
}
// 另一个示例,不使用解构,TS 会显示错误
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={componentData.subTitle}
        // TS 在这里显示错误
        randomProp="string"
      />
    </div>
  );
}

如果您需要更多信息,请参考以下两个 CodeSandbox 示例:

  1. CodeSandbox 1
  2. CodeSandbox 2
英文:

Looking at the following code example

// Component.tsx file
import React from &quot;react&quot;;

export const Component = ({ title, subTitle }: Props) =&gt; {
  return (
    &lt;div&gt;
      &lt;h1&gt;{title}&lt;/h1&gt;
      &lt;p&gt;{subTitle}&lt;/p&gt;
    &lt;/div&gt;
  );
};

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

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

As you can see, on line 5 in App.tsx the componentData object is typed as any.
On line 11, in the return of the App.tsx I send that object as props for another component using the spread operator.

If I do that, TS aborts checking the rest of the props that are sent, as you can see randomProp=&quot;string&quot; does not throw an error.

If I remove the :any typing from the object on line 5, then TS marks the randomProp=&quot;string&quot; as non existing on the props of Component (I expected this to happen with :any on line 5 as well).

The follwing example does not use destructuring and TS shows the error this way

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

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

For more information:

Please have a look at this CodeSandbox: https://codesandbox.io/s/elated-worker-td2g0u?file=/src/App.tsx .

And also this one: https://codesandbox.io/s/shy-monad-ygkloq?file=/src/App.tsx

Is this intended behaviour or a bug? How does this work underneath?

答案1

得分: 1

「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, // 可以
    subTitle: componentData.fsfsdjfiwjwi,  // 可以
    randomProp: "abc" // 错误!
};

现在,让我们比较一下当你将一个具有「any」类型的值扩展到对象字面量时会发生什么:

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" // 可以
};

所以就是这样。如果你将「any」扩展到对象字面量中,它将「感染」整个对象并变为「any」。但如果你单独将 any 类型的属性添加到对象中,any 类型将仅限于这些属性。

英文:

The any type is explicitly meant to turn off type checking, and it tends to be "infectious" or "contagious" in that combining any with other types often results in any. So if you want type checking, a good general rule is: avoid any as much as possible.


Still, if you need to use it, you should learn more detail of how it interacts with other types and when it "takes over" other types and when it doesn't. Let's start with this:

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

Since componendData is of type any, the compiler will let you index into it with any key you want, and the resulting property will also be of type any:

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

If you make an object literal with any-typed values as property values, only those properties will have any types; it doesn't "infect" the whole object:

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

So if you try to assign such an object literal to a variable of type Props, the compiler will happily accept the any properties (even though it can't verify them, which is the point of any), but it will catch excess properties, because the object literal is aware of their existence:

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

Now let's compare to what happens when you spread an any-typed value into an object literal:

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

Conceptually a spread will result in something very much like the intersection of the spreaded object type with the type for the rest of the object literal. (For spreads with known keys the compiler will try to represent overwritten properties more accurately, but for generic or other unknown-key types, it uses the intersection.) And the intersection of any with any other type is just any:

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

So that objSpread value is now of type any. The compiler has completely forgotten about randomProp. And that means if you assign such an object literal to a value of type Props, the compiler will not see any excess properties, because any doesn't have any "excess" properties:

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

So there you go. If you spread an any into an object literal it "infects" the whole object and becomes any. But if you add properties of type any individually to an object, the any type will be confined to just those properties.

Playground link to code

huangapple
  • 本文由 发表于 2023年3月9日 19:38:40
  • 转载请务必保留本文链接:https://go.coder-hub.com/75684083.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定