switch case语句中的Typescript类型安全性

时间:2016-11-16 08:31:35

标签: typescript typescript2.0

我正在使用Redux,我正在尝试使我的减速器类型安全。我在ngrx-store / example应用程序中找到了一些代码示例,他们完全成功地做到了这一点。 (https://github.com/ngrx/example-app/blob/master/src/app/actions/book.ts

在我自己的项目中集成它时,我注意到一些奇怪的东西,我无法解释。检查以下代码示例(内联注释):

// Action has a type and payload property
interface Action {
    type: string;
    payload?: any;
}

// Here I declare the action types as plain strings
const FIRST = "FIRST";
const SECOND = "SECOND";

// I create classes for every action with there respective types
class FirstAction implements Action {
    public type = FIRST;
    payload: { id: number };

    public constructor(id: number) {
        this.payload = { id };
    }
}

class SecondAction implements Action {
    public type = SECOND;

    public constructor() { }
}

// Create a union type
type Actions = FirstAction | SecondAction;

// Use the union type as type parameter in my function
function test(action: Actions): void {
    switch (action.type) {
        case FIRST:
                    // compiler will complain it cannot find the payload 
                    // property on Actions
            let temp = action.payload.id;
        case SECOND:
        // empty
        default:
        //empty
    }
}

如果我将FIRST和SECOND属性的定义替换为以下内容,它确实有效。

export function type<T>(label: T | ''): T {
    return <T>label;
}


const FIRST = type("FIRST");
const SECOND = type("SECOND");

据我所知,type函数只将字符串强制转换为字符串。为什么代码在调用type函数时有效,但在立即声明字符串时却没有?

这是一个typescript playground example,你可以在其中评论定义(首先是工作版本)。

3 个答案:

答案 0 :(得分:2)

这是因为TSC编译器无法区分这两个值:

const FIRST = "FIRST";
const SECOND = "SECOND";

它都是string类型,因此TSC不知道哪个属于什么。你必须给它一个类型,并且通过使用type函数将它作为你正在做的事情。

但如果按照以下方式编写它会更容易:

const FIRST: "FIRST" = "FIRST";
const SECOND: "SECOND" = "SECOND";

Typescript playground

答案 1 :(得分:0)

它只适用于const,而不是正则表达式,没有变量。

switch(variable_expression) { 
   case constant1: { 
      //code; 
      break; 
   } 
   case constant2: { 
      //code; 
      break; 
   } 
   default: { 
      //code; 
      break; 
   } 
}

答案 2 :(得分:-1)

我会使用as运算符:

let temp = (action as FirstAction).payload.id;

其他一些想法:
- 如果你已经拥有Actions,那么你真的需要Action吗?    你的行动类实施了吗? - 将每个案例的减速器提取到一个函数中可能有助于提高可读性和单元测试 - 请记住,reducer采取状态和操作,并返回状态(我知道你只是简化了你的例子)。

function test(state: State, action: Action): State {
    switch (action.type) {
        case FIRST:
            return handleFirst(state, action as FirstAction);
        case SECOND:
            return handleSecond(state, action as SecondAction);
        default:
            return state;
    }
}

function handleFirst(state: State, action: FirstAction): State {
    let temp = action.payload.id;
    // ...
}

// ...