【问题标题】:Switch for specific type in TypeScript在 TypeScript 中切换特定类型
【发布时间】:2018-11-19 08:23:57
【问题描述】:

我有一个接口Action

interface Action {}

还有这个ActionSpecificAction的实现:

class SpecificAction implements Action {
   payload?: Any
}

是否可以在 TS 中构造一个 switch 操作符,像这样:

let action: Action
switch (action) {
   case SpecificAction: //it works
       console.log(action.payload) // it doesn't 
}

在这种情况下是否有可能知道该操作已经属于SpecificAction 类型?

【问题讨论】:

  • 你能用discriminated union吗?
  • 我可以,但我真的不知道它有多好。我仍然需要检查一些属性而不是自己输入
  • @VladyslavZavalykhatko 是的,你仍然需要检查一些属性,但这就是编译器支持的开关和类型保护
  • @TitianCernicova-Dragomir,很公平)
  • 在实践中action 是实现Action 的类的实例,还是只是一个常规对象?如果是前者,我的回答是否有帮助,还是我误解了您要查找的内容?

标签: typescript


【解决方案1】:

最好使用带有类型保护的if 语句。

let action: Action = ...;
if (isSpecificAction(action)) {
    console.log(action.payload);
}

function isSpecificAction(action: any): action is SpecificAction {
    return action.payload;
}

归根结底,TypeScript 仍然是 JavaScripty,switch 语句被转译为常规 JS switch

switch 语句首先计算其表达式。然后它查找第一个 case 子句,其表达式的计算结果与输入表达式的结果相同(使用严格比较 ===)

所以在你的情况下:

interface Action {}
class SpecificAction implements Action {
   payload?: any
}

let action: Action
switch (action) {
   case SpecificAction: //it works
       console.log(action.payload) // it doesn't 
}

action 将被评估并与 SpecificAction 进行比较。据推测,actionSpecificAction(或实现Action 接口的某个对象)的实例

使用switch,您可以这样做:

let a: Action = new SpecificAction();
let b: Action = a;

switch (a) {
    case b:
        console.log("Worked");
}

计算表达式 a 并与表达式 b(和 a === b,因此我们点击 console.log)进行比较,但这显然不是您要查找的内容。

如果你想检查一个实例是否属于特定类型(re: class),那么你应该使用类型保护。 switch/case 是错误的构造。


或者,为什么不使用instanceof

interface Action { };
class SpecificAction implements Action {}
class NotSpecificAction implements Action {}

let action: Action = new SpecificAction();
console.log(action instanceof SpecificAction); // true
console.log(action instanceof NotSpecificAction); // false

【讨论】:

  • 这不能回答问题,还是存在技术错误? (好奇投反对票)
  • 这是一个很好的答案,但值得指出的是,在 OP 中使用标记界面时存在明显的误解,而不是重复它。空接口在 typescript 中是没有意义的,因为它无法统一实现者,这可以从 implements 子句的可选性质中得到证明
  • @AluanHaddad 标记界面可能只是一个小例子。他的实际Action 界面很有可能提供更多功能
  • 这是可能的,但考虑到问题的性质和措辞,我认为指出这一点很重要。许多人不了解类型擦除,对此感到困惑。我会写我自己的答案,但我喜欢你的。 +1 无论如何
【解决方案2】:

目前看起来有几个选项,所有选项都有一些缺点

  • 可区分联合 docs stackblitz,但您需要一个专用属性作为鉴别器
interface Action {}

class SpecificAction implements Action {
  kind: "specific";
  payload?: any;
}

class ToggleAction implements Action {
  kind: "toggle";
  toggle: boolean;
}

let action: SpecificAction | ToggleAction;
switch (action.kind) {
  case "specific":
    console.log(action.payload) // it works 
    break;
  case "toggle":
    console.log(action.toggle) // it works 
    break;        
}
  • 用户定义的类型保护 docs stackblitz,但您需要 if 语句而不是 switch
interface Action {}

class SpecificAction implements Action {
  payload?: any;
}

class ToggleAction implements Action {
  toggle: boolean;
}

let isSpecific = (p: any): p is SpecificAction => !!p.payload
let isToggle = (p: any): p is ToggleAction => !!p.toggle

let action: Action;
if (isSpecific(action)) {
  console.log(action.payload) // it works 
} else if (isToggle(action)) {
  console.log(action.toggle) // it works 
}
  • 构造函数属性githubstackblitz,但您暂时需要转换为所需的类型
interface Action { }

class SpecificAction implements Action {
  payload?: any;
}

class ToggleAction implements Action {
  toggle: boolean;
}

switch (action.constructor) {
  case SpecificAction:
    console.log((<SpecificAction>action).payload) // it kinda works 
    break;
  case ToggleAction:
    console.log((<ToggleAction>action).toggle) // it kinda works 
    break;
  }

【讨论】:

  • 这个答案应该指出了标记界面背后的问题误解
  • 您想详细说明您的担忧吗?
  • 空的interface 在打字稿中是没有意义的,因为它绝不会统一实施者,implements 子句的可选性质证明了这一点。
  • 我很确定我们错过了原始问题中的一些上下文,但为了完整起见,我想这个 github.com/Microsoft/TypeScript/wiki/FAQ#what-is-type-erasure 应该可以帮助任何未来的读者
猜你喜欢
  • 2021-10-17
  • 2021-08-06
  • 2016-08-31
  • 2018-09-19
  • 1970-01-01
  • 2019-07-26
  • 1970-01-01
  • 2015-06-16
  • 2019-03-26
相关资源
最近更新 更多