【问题标题】:Casting types in a switch在开关中投射类型
【发布时间】:2021-10-24 21:49:15
【问题描述】:

我正在尝试模块化我的类型转换。这是我的代码:

export enum detailsDataTypes {
  MACHINE = 'MACHINE',
  USER = 'USER',
  ABSTRACT = 'ABSTRACT',
}

export type sharedTypes = {
  name?: string;
  OEECategory?: string;
  OAECategory?: string;
  color?: string;
  type?: detailsDataTypes;
};

export type AbstractData = sharedTypes & {
  layer: string;
};

export type UserData = sharedTypes & {
  timeout: number;
  stateAfterTimeout: string;
  accessCanChoose: string;
  accessCanOverride: string;
};

export type MachineData = sharedTypes & {
  stateCategory: string;
  timeout: number;
  stateAfterTimeout: string;
  accessCanChoose: string;
  accessCanOverride: string;
  canOverrideOnlyByStates: boolean;
};

type DetailsData = AbstractData | UserData | MachineData;

export const detailsDataCaster = (data: DetailsData): DetailsData | null => {
  switch (data.type) {
    case detailsDataTypes.ABSTRACT:
      return <AbstractData>data;
    case detailsDataTypes.MACHINE:
      return <MachineData>data;
    case detailsDataTypes.USER:
      return <UserData>data;
    default:
      console.log('Wrong data type. Go yell at backend team or check your mocks');
      return null;
  }
};

我想要实现的是告诉 TS 我要返回 AbstractDataUserDataMachineDatanull。但是现在,它不喜欢我这样做:

export const Details: FC<IDetails> = ({ data: $data = mockUserData }): ReactElement => {
  const data = detailsDataCaster($data);
  switch (data.type) {
    case detailsDataTypes.ABSTRACT:
      return <>Abstract</>;
    case detailsDataTypes.MACHINE:
      return <>Machine</>;
    case detailsDataTypes.USER:
      return <UserDisplay data={data} />; // error is here
    default:
      return <div />;
  }
};

错误

Type 'DetailsData' is not assignable to type 'UserData'.
  Type 'AbstractData' is not assignable to type 'UserData'.
    Type 'AbstractData' is missing the following properties from type '{ timeout: number; stateAfterTimeout: string; accessCanChoose: string; accessCanOverride: string; }': timeout, stateAfterTimeout, accessCanChoose, accessCanOverride

据我了解,| 在 TS 中并不意味着 OR。我知道有 AND &amp;,但这没有任何意义。我以为如果我施放,我会返回什么就很清楚了,但我发现它不是那么简单。我错过了什么?

【问题讨论】:

  • 请考虑修改这个问题中的代码以构成一个minimal reproducible example,当它放入像The TypeScript Playground (link, this is an IDE with your code in it)这样的独立IDE时,清楚地表明您面临的问题没有不相关的错误(如未导入/未声明的类型或值)。这将使那些想要帮助您的人立即着手解决问题,而无需首先重新创建它。它将使您得到的任何答案都可以针对定义明确的用例进行测试。
  • detailsDataCaster 中的类型断言与为 data 推断的类型没有区别。我认为您正在尝试创建一个discriminated union,因此可以通过检查data.type 来缩小范围,但这不是您所拥有的。

标签: javascript reactjs typescript types


【解决方案1】:

如果每个扩展sahredTypes 的类型都指定自己的type,那么开关就会神奇地起作用:

export enum detailsDataTypes {
  MACHINE = 'MACHINE',
  USER = 'USER',
  ABSTRACT = 'ABSTRACT',
}

export interface sharedTypes {
  name?: string;
  OEECategory?: string;
  OAECategory?: string;
  color?: string;
  type?: detailsDataTypes;
};

export interface AbstractData extends sharedTypes {
  type: detailsDataTypes.ABSTRACT;

  layer: string;
};

export interface UserData extends sharedTypes {
  type: detailsDataTypes.USER;

  timeout: number;
  stateAfterTimeout: string;
  accessCanChoose: string;
  accessCanOverride: string;
};

export interface MachineData extends sharedTypes {
  type: detailsDataTypes.MACHINE;

  stateCategory: string;
  timeout: number;
  stateAfterTimeout: string;
  accessCanChoose: string;
  accessCanOverride: string;
  canOverrideOnlyByStates: boolean;
};

type DetailsData = AbstractData | UserData | MachineData;

Playgound link 表明从开关中正确推断出类型。

【讨论】:

  • 在这种情况下是否有理由将type?: detailsDataTypes; 留在sharedTypes 中?
  • 是的,如果是共享类型,你可以检测它的类型
  • 但是如果我们已经在每个接口中放入了类型,为什么我们还需要在 shared 中呢?
  • 如果您曾经分配过sharedTypes 的变量类型,这将是有益的,但您不需要在父级中使用它。更重要的是,如果您只在应用程序的其余部分分享DetailsData。我只是想把它放在那里,别无其他
【解决方案2】:

您可能希望像这样为每种类型添加判别值...

export type AbstractData = sharedTypes & {
  layer: string;
  type: detailsDataTypes.ABSTRACT; // add this field, it can only be of this literal value
};

export type UserData = sharedTypes & {
  timeout: number;
  stateAfterTimeout: string;
  accessCanChoose: string;
  accessCanOverride: string;
  type: detailsDataTypes.USER; // add this field
};

export type MachineData = sharedTypes & {
  stateCategory: string;
  timeout: number;
  stateAfterTimeout: string;
  accessCanChoose: string;
  accessCanOverride: string;
  canOverrideOnlyByStates: boolean;
  type: detailsDataTypes.MACHINE; // add this field
};

type DetailsData = AbstractData | UserData | MachineData;
function Details(data: DetailsData) {
    switch (data.type) {
        case detailsDataTypes.ABSTRACT:
            data; // data is AbstractData
            break;
            
        case detailsDataTypes.MACHINE:
            data; // data is MachineData
            break;

        case detailsDataTypes.USER:
            data; // data is UserData
            break;

        default:
            data; // data is never (no more possibilities left!)
            break;
    }
}

这种技术称为discriminated unions

【讨论】:

    猜你喜欢
    • 2012-06-09
    • 2011-09-20
    • 1970-01-01
    • 1970-01-01
    • 2018-03-19
    • 1970-01-01
    • 2012-02-13
    • 2011-08-11
    • 2017-03-17
    相关资源
    最近更新 更多