首页 > 解决方案 > 为什么 Typescript 会抛出错误消息:类型上不存在属性?

问题描述

我创建了两个代码示例。它们之间的唯一区别是我传递给 switch 运算符的表达式。

在第一种情况下,我使用对象属性。它工作正常。

在第二种情况下,我创建了一个type变量。Typescript 会抛出错误消息:

“操作”类型上不存在属性“名称”。

类型 '{ type: "reset"; 上不存在属性 'name' }'。

为什么会这样?

对象属性action.type和变量type属于同一类型'reset' | 'update'

interface State {
    name: string;
    cars: any[];
}

type Action = { type: 'reset' } | { type: 'update', name: string };

function reducer(state: State, action: Action): State {
    switch (action.type) {
        case 'update':
            return { ...state, name: action.name };
        case 'reset':
            return {...state, cars: [] };
        default:
            throw new Error();
    }
}

interface State {
    name: string;
    cars: any[];
}

type Action = { type: 'reset' } | { type: 'update', name: string };

function reducer(state: State, action: Action): State {
    /**
    * Create a 'type' variable
    */
    const { type } = action;

    switch (type) {
        case 'update':
            return { ...state, name: action.name };
    /**
    * Typescript will throw an error message
    * Property 'name' does not exist on type 'Action'.
    * Property 'name' does not exist on type '{ type: "reset"; }'.
    */
        case 'reset':
            return {...state, cars: [] };
        default:
            throw new Error();
    }
}

图片说明

标签: typescriptredux

解决方案


基本上,Typescript 不会跟踪变量类型actiontype;之间的关系。当type's 类型被缩小时(例如在 a caseofswitch语句中),它也不会缩小action's 类型。

在赋值const { type } = action;时,编译器会推断type: Action['type'],这恰好是'reset' | 'update'. 后来,case表达式并没有缩小 的类型,action因为没有对 进行类型保护检查action

为了让它按照您希望的方式运行,编译器必须引入一个类型变量T extends Action['type']和推断type: T,同时缩小action到类型: Action & { type: T }。那么当type' 的类型被缩小时,T它本身也必须被缩小,所以效果会传播到action' 类型,这将涉及T

在每个变量赋值中引入一个像这样的新类型变量,并且控制流缩小类型变量的上限,会使类型检查算法大大复杂化。这也会使推断类型变得非常复杂,使用户更难理解;所以 Typescript 不这样做是合理的。一般来说,类型检查器并不能证明代码的所有可证明属性,这是一个示例。


推荐阅读