为什么类型保护无法通过类型检查
Why is a type guard failing type-check
在 React 项目中,我使用类型保护来确保我传递的 prop 具有适当的类型。这似乎有效,因为 TypeScript 没有在我的 IDE 中引发任何错误。但是当 运行 作为命令进行类型检查时,编译器在该 prop 上失败。
我有一个正在渲染子组件的组件。该子组件需要接收具有自定义类型的道具,在枚举 AuthorizedType
中声明。为了确保传递的 prop 有效,我创建了一个类型保护 isAuthorizedType
。如果类型无效,组件将不会呈现。
因为当类型保护失败时子组件没有呈现,TypeScript 没有在我的 IDE 中引发任何错误。但是当 运行 作为命令进行类型检查时,编译器会引发错误:
TS2322: Type 'string' is not assignable to type 'AuthorizedType'.
我想知道为什么在 IDE 中没有出现错误,但在 运行 类型检查时出现。我知道类型保护执行 runtime 检查,我想知道它的确切含义以及是否相关。
代码如下:
// ParentComponent.tsx
export enum AuthorizedType {
First = 'first',
Second = 'second',
}
function isAuthorizedType(value: any): value is AuthorizedType {
return Object.values(AuthorizedType).indexOf(value) !== -1;
}
function ParentComponent({value}: {value: string}) {
const isValidType = isAuthorizedType(value);
if (!isValidType) {
return null;
}
return (<ChildComponent value={value} />)
}
// ChildComponent.tsx
import {AuthorizedType} from '..';
function ChildComponent({value}: {value: AuthorizedType}) {
switch(value) {
case AuthorizedType.First:
return <div>first</div>
case AuthorizedType.Second:
return <div>second</div>
}
}
通过将类型保护移到子组件中,类型检查不再引发任何错误:
// ChildComponent.tsx
import {AuthorizedType} from '..';
function isAuthorizedType(value: any): value is AuthorizedType {
return Object.values(AuthorizedType).indexOf(value) !== -1;
}
function ChildComponent({value}: {value: string}) {
const isValidType = isAuthorizedType(value);
if (!isValidType) {
return null;
}
switch(value) {
case AuthorizedType.First:
return <div>first</div>
case AuthorizedType.Second:
return <div>second</div>
}
}
我想知道为什么。
您的 ChildComponent
被标记为期望 AuthorizedType
为 props
。但是,在 ParentComponent
中,您将 child 与 { prop: AuthorizedType }
称为 props
。 AuthorizedType
extends string
并且不能与类型 { prop: AuthorizedType }
重叠,因此你会得到一个 TS2322 错误。
它在你的情况下消失的原因是当你将 isAuthorizedType
调用移动到你的 child 组件时,它正在尝试检查它接收到的 props
值(即 { prop: AuthorizedType }
) 是一个AuthorizedType
(不可能是,因为object
不扩展string
),分配false
到 isValidType
,并呈现 null
。
所以问题是由于在 ChildComponent
中期望 AuthorizedType
为 props
而实际上您应该期望 { prop: AuthorizedType }
。当然,您可能希望使用更具描述性的名称来命名 属性,例如“value”:
function Child(props: { value: AuthorizedType }) {
switch(prop.value) {
case AuthorizedType.First:
return <div>first</div>
case AuthorizedType.Second:
return <div>second</div>
}
}
以下格式(使用 object destructuring)与上述格式相同,但可能更符合人体工程学:
interface ChildProps {
value: AuthorizedType
}
function Child({ value }: ChildProps) {
switch(value) {
case AuthorizedType.First:
return <div>first</div>
case AuthorizedType.Second:
return <div>second</div>
}
}
然后您将调用 Child
,例如(参见 jsx ...
spread attributes):
function ParentComponent(props: ChildProps) {
return isAuthorizedType(props.value)
? <Child {...props} />
: null;
}
看完您的编辑后,我认为最好完全写出另一个答案。你的程序逻辑在我看来并没有缺陷,并且在验证之后,所以我相信你在 TypeScript 的控制流分析中发现了一个错误。
Here's a playground link 在等于或低于 4.3.5
的 TS 版本上 按预期 工作,但在 上等于或高于 4.4.4
的版本。在错误版本 (>=4.4.4
) 上,编译器将 ParentComponent
的 return 类型计算为 null
,但实际上调用发出的 ParentComponent()
函数 returns "first"
.
我当然可能遗漏了配置标志或影响 CFA 工作方式的东西,或者版本 4 中可能有意更改。4.x,所以请检查您是否可以找到相关的错误 in GitHub. If not, I think you should file a report about this.
在 React 项目中,我使用类型保护来确保我传递的 prop 具有适当的类型。这似乎有效,因为 TypeScript 没有在我的 IDE 中引发任何错误。但是当 运行 作为命令进行类型检查时,编译器在该 prop 上失败。
我有一个正在渲染子组件的组件。该子组件需要接收具有自定义类型的道具,在枚举 AuthorizedType
中声明。为了确保传递的 prop 有效,我创建了一个类型保护 isAuthorizedType
。如果类型无效,组件将不会呈现。
因为当类型保护失败时子组件没有呈现,TypeScript 没有在我的 IDE 中引发任何错误。但是当 运行 作为命令进行类型检查时,编译器会引发错误:
TS2322: Type 'string' is not assignable to type 'AuthorizedType'.
我想知道为什么在 IDE 中没有出现错误,但在 运行 类型检查时出现。我知道类型保护执行 runtime 检查,我想知道它的确切含义以及是否相关。
代码如下:
// ParentComponent.tsx
export enum AuthorizedType {
First = 'first',
Second = 'second',
}
function isAuthorizedType(value: any): value is AuthorizedType {
return Object.values(AuthorizedType).indexOf(value) !== -1;
}
function ParentComponent({value}: {value: string}) {
const isValidType = isAuthorizedType(value);
if (!isValidType) {
return null;
}
return (<ChildComponent value={value} />)
}
// ChildComponent.tsx
import {AuthorizedType} from '..';
function ChildComponent({value}: {value: AuthorizedType}) {
switch(value) {
case AuthorizedType.First:
return <div>first</div>
case AuthorizedType.Second:
return <div>second</div>
}
}
通过将类型保护移到子组件中,类型检查不再引发任何错误:
// ChildComponent.tsx
import {AuthorizedType} from '..';
function isAuthorizedType(value: any): value is AuthorizedType {
return Object.values(AuthorizedType).indexOf(value) !== -1;
}
function ChildComponent({value}: {value: string}) {
const isValidType = isAuthorizedType(value);
if (!isValidType) {
return null;
}
switch(value) {
case AuthorizedType.First:
return <div>first</div>
case AuthorizedType.Second:
return <div>second</div>
}
}
我想知道为什么。
您的 ChildComponent
被标记为期望 AuthorizedType
为 props
。但是,在 ParentComponent
中,您将 child 与 { prop: AuthorizedType }
称为 props
。 AuthorizedType
extends string
并且不能与类型 { prop: AuthorizedType }
重叠,因此你会得到一个 TS2322 错误。
它在你的情况下消失的原因是当你将 isAuthorizedType
调用移动到你的 child 组件时,它正在尝试检查它接收到的 props
值(即 { prop: AuthorizedType }
) 是一个AuthorizedType
(不可能是,因为object
不扩展string
),分配false
到 isValidType
,并呈现 null
。
所以问题是由于在 ChildComponent
中期望 AuthorizedType
为 props
而实际上您应该期望 { prop: AuthorizedType }
。当然,您可能希望使用更具描述性的名称来命名 属性,例如“value”:
function Child(props: { value: AuthorizedType }) {
switch(prop.value) {
case AuthorizedType.First:
return <div>first</div>
case AuthorizedType.Second:
return <div>second</div>
}
}
以下格式(使用 object destructuring)与上述格式相同,但可能更符合人体工程学:
interface ChildProps {
value: AuthorizedType
}
function Child({ value }: ChildProps) {
switch(value) {
case AuthorizedType.First:
return <div>first</div>
case AuthorizedType.Second:
return <div>second</div>
}
}
然后您将调用 Child
,例如(参见 jsx ...
spread attributes):
function ParentComponent(props: ChildProps) {
return isAuthorizedType(props.value)
? <Child {...props} />
: null;
}
看完您的编辑后,我认为最好完全写出另一个答案。你的程序逻辑在我看来并没有缺陷,并且在验证之后,所以我相信你在 TypeScript 的控制流分析中发现了一个错误。
Here's a playground link 在等于或低于 4.3.5
的 TS 版本上 按预期 工作,但在 上等于或高于 4.4.4
的版本。在错误版本 (>=4.4.4
) 上,编译器将 ParentComponent
的 return 类型计算为 null
,但实际上调用发出的 ParentComponent()
函数 returns "first"
.
我当然可能遗漏了配置标志或影响 CFA 工作方式的东西,或者版本 4 中可能有意更改。4.x,所以请检查您是否可以找到相关的错误 in GitHub. If not, I think you should file a report about this.