1

我正在尝试做一些新的事情,但我有一个小问题:

我有以下=>

export interface SynchGroupSubject {
  type: SynchGroupEvent;
  target: any;
}

export enum SynchGroupEvent {
  ADD_MAP,
  REMOVE_MAP
}

在一项服务中,我有以下内容

  public createNewSynchGroup(id?: string): Observable<SynchGroupSubject> {
    const subject = new Subject<SynchGroupSubject>();
    this.synchronizedGroupSubject.set(id, subject);
    return subject.asObservable();
  }

现在,有了这个我可以订阅和关注更新事件 =>

this.synchObs = this.mapSynchService.createNewSynchGroup(this.synchID);
this.synchObs.subscribe(event => this.synchEventHandler(event));
synchEventHandler(event: SynchGroupSubject) {
 switch (event.type) {
  case SynchGroupEvent.ADD_MAP:
       //event.target is type any
    break;
  default:
  break;

 }
}

问题是,我想输入目标,以便在切换时,我知道我要处理什么。现在,目标类型是 any,但我想绑定与类型事件相关的类型。

简单的方法是制作多种类型,如下所示:

export interface ADD_MAP_TYPE {
  type: SynchGroupEvent;
  target: MyTypedTarger;
}

synchEventHandler(event: SynchGroupSubject) {
 switch (event.type) {
  case SynchGroupEvent.ADD_MAP:
       event.target = event.target as ADD_MAP_TYPE;
    break;
  default:
  break;

 }

但我想要一些 SynchGroupEventType.ADD_MAP。

我考虑过使用命名空间,但它是矫枉过正。

另外,如果我想调度和事件

  public dispatchSynchedEvent(id: string, type: SynchGroupEvent, value: any) 
  {
     myElement.next({
       type: type,
       target: value,
     })
  }

到这里,我的值是any,我希望我能到以下

  public dispatchSynchedEvent(id: string, type: SynchGroupEvent, value: any) 
  {
     myElement.next({
       type: type,
       target: value as , // get the type of the value thx to the SynchGroupEvent type
     })
  }
4

2 回答 2

2

看起来您确实应该SynchGroupSubject. 现在,您正在使用类型断言来告诉编译器target您在检查type属性时拥有哪种类型,并且您必须手动跟踪该映射。受歧视的工会几乎免费为您提供该功能。让我们看一个例子:

export enum SynchGroupEvent {
    ADD_MAP,
    REMOVE_MAP
}

// made-up example target interfaces    
interface MyAddTarget { a: string };
interface MyRemoveTarget { r: number };

// a discriminated union
type SynchGroupSubject = {
    type: SynchGroupEvent.ADD_MAP,
    target: MyAddTarget
} | {
    type: SynchGroupEvent.REMOVE_MAP,
    target: MyRemoveTarget
}

function synchEventHandler(event: SynchGroupSubject) {
    switch (event.type) {
        case SynchGroupEvent.ADD_MAP:
            event.target; // known by the compiler to be MyAddTarget
            event.target.a; // okay
            event.target.r; // error
            break;
        default:
            event.target; // known by the compiler to be MyRemoveTarget
            event.target.a; // error
            event.target.r; // okay
            break;
    }
}

链接到 Playground 中的代码

这一切都按你想要的方式工作,对吧?语句中的个别情况由编译器自动推断为具有与属性相对应switch的强类型。 event.targetevent.type

好的,希望有帮助。祝你好运!

于 2019-04-19T10:45:17.583 回答
1

@jcalz 的答案是正确的方法,应该是公认的答案。请先阅读他的回答以了解歧视性工会的概念。

我想为 jcalz 的答案提供一种补充方法,一种“技巧”,在某些情况下可能有用。

现在您已经声明了枚举,如果您SynchGroupSubject有一个固定的形状{ type, target },那么您可能希望以这种方式声明您的目标类型。这种语法对我来说更具可读性。

enum SynchGroupEvent {
    ADD_MAP,
    REMOVE_MAP
}

type SynchGroupEventTarget = {
    [SynchGroupEvent.ADD_MAP]: { a: string };
    [SynchGroupEvent.REMOVE_MAP]: { r: number }
}

这里的诀窍是,我们可以使用映射类型从上面的两种类型中派生出可区分的联合,而不是手动编写联合类型SynchGroupSubject

type SynchGroupSubject = {
    [K in SynchGroupEvent]: {
        type: K;
        target: SynchGroupEventTarget[K];
    }
}[SynchGroupEvent]

这种方法的一个好处是代码的可重用性。如果这种模式经常出现,您还可以将其推广到实用程序类型。

type BuildSubject<Enum extends keyof TargetMap, TargetMap> = {
    [K in Enum]: {
        type: K;
        target: TargetMap[K]
    }
}[Enum]

// usage
type SynchGroupSubject = BuildSubject<SynchGroupEvent, SynchGroupEventTarget>
type OtherSubject = BuildSubject<OtherEvent, OtherEventTarget>

旁注,这些Subject类型具有固定形状的前提很重要,否则我的方法将不起作用。

于 2019-04-19T12:07:24.303 回答