英文:
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 示例:
英文:
Looking at the following code example
// 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>
);
}
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="string"
does not throw an error.
If I remove the :any
typing from the object on line 5, then TS marks the randomProp="string"
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 "./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>
);
}
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: "Hello",
subTitle: "Hello from comp"
};
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: "abc"
};
/* 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: "abc" // error!
};
Now let's compare to what happens when you spread an any
-typed value into an object literal:
const objSpread = {
...componentData,
randomProp: "abc"
};
// 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 & { 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: "abc" // 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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论