为什么打字稿会引发错误消息:类型上不存在属性?

时间:2019-11-14 15:56:24

标签: typescript redux

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

在第一种情况下,我使用对象属性。而且效果很好。

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

  

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

     

属性'name'在类型'{不存在,类型:“ reset”; }'。

为什么会这样?

对象属性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();
    }
}

Image description

2 个答案:

答案 0 :(得分:2)

基本上,Typescript不会跟踪变量actiontype的类型之间的关系;当type的类型变窄时(例如在case语句的switch中),它也不会变窄action的类型。

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

要使此行为按照您希望的方式运行,编译器将必须引入类型变量T extends Action['type']并推断type: T,同时将action缩小为类型{{ 1}}。然后,当: Action & { type: T }的类型变窄时,type本身必须变窄,因此效果将传播到T的类型,其中涉及action

在每个变量分配中引入像这样的新类型变量,并且控制流会缩小类型变量的上限,这将极大地使类型检查算法复杂化。这也将使推断出的类型变得非常复杂,使用户难以理解它们。因此Typescript不执行此操作是合理的。一般来说,类型检查器不会证明代码的每个可证明属性,这是一个示例。

答案 1 :(得分:1)

当您引用参数时,它可以从整个对象中推断出类型,但是当您创建一个常量时,您将类型限制为简单地变成"reset" | "update",而Action对象类型信息的其他位将丢失。