以某种方式避免在打字稿中输入类型

时间:2018-03-19 08:26:06

标签: typescript generics casting typescript-typings

我想知道是否可以使用类型保护或任何其他聪明的方法来确定我从函数中获取的类型,因为所有TypeA对象都标有Type:&# 34;类型A"以及" TypeA" -object内部。我想避免类型转换的语句在这个相当长的代码块的最后一行:

type ControlPageType = "TypeA" | "TypeB";

interface ControlPageDto {
  type: ControlPageType;
}

interface TypeAData extends ControlPageDto {
  foo: string;
}

interface TypeBData extends ControlPageDto {
  bar: number;
}

export interface ControlPageState<T extends ControlPageDto> {
  readonly type: ControlPageType;
  readonly checksum: string;
  readonly backendState: T;
}

export interface TypeA extends ControlPageState<TypeAData> {
  type: "TypeA";
}

export interface TypeB extends ControlPageState<TypeAData> {
  type: "TypeB";
}

export interface ControllerState<D extends ControlPageDto, S extends ControlPageState<D>> {
  pages: S[];
}

export interface ApplicationState {
  controllers: { [K in ControlPageType]: ControllerState<any, any> };
  currentChecksum: string;
}


export const findCurrentPage = (state: ApplicationState, type: ControlPageType): ControlPageState<any> => {
  const checksum = state.currentChecksum;

  if (checksum === null) {
    return null;
  }

  const controllerState = state.controllers[type];
  const pages = controllerState.pages;

  for (const bilde of pages) {
    if (bilde.checksum === checksum) {
      return bilde;
    }
  }

  return null;
};

const usage = (): void => {
  const sampleA1: TypeAData = { type: "TypeA", foo: "FOO" };
  const sampleA2: TypeAData = { type: "TypeA", foo: "OOF" };
  const sampleB1: TypeBData = { type: "TypeB", bar: 123 };

  const sampleData: ApplicationState = {
    controllers: {
      TypeA: {
        pages: [{ type: "TypeA", checksum: "1", customData: sampleA1 }, { type: "TypeA", checksum: "2", customData: sampleA2 }]
      },
      TypeB: { pages: [{ type: "TypeB", checksum: "A", customData: sampleB1 }] }
    },
    currentChecksum: "1"
  };

  const page = findCurrentPage(sampleData, "TypeA") as TypeA; // TODO Avoid type casting
};

1 个答案:

答案 0 :(得分:1)

有几种方法可以做到这一点。

最简单的方法是为每个常量提供几个重载:

export function findCurrentPage(state: ApplicationState, type: 'TypeA'): TypeA
export function findCurrentPage(state: ApplicationState, type: 'TypeB'): TypeB
export function findCurrentPage(state: ApplicationState, type: ControlPageType): ControlPageState<any> {
    //...
    return null;
};

这样做的缺点是必须为每个定义一个重载,你必须在几个地方维护类型,ControlPageType和函数。

更干的解决方案是定义一个将字符串映射到类型的对象类型,并使用它来获取基于地图键的类型:

type ControlPageTypeMap = {
    "TypeA": TypeA
    "TypeB": TypeB
};

type ControlPageType = keyof ControlPageTypeMap;
export function findCurrentPage<K extends ControlPageType>(state: ApplicationState, type: K): ControlPageTypeMap[K] {
    //...
    return null;
};

或者如果您想要更加安全并避免有人错误地映射"TypeA": TypeB的情况,您可以声明一个约束映射有效的辅助函数。辅助函数和辅助变量只是帮助进行类型推断,没有与之关联的运行时行为。

// Helper to constrain each entry in the map type to have a property named type that has the same type P as the current key  
function typeMapValidator<T extends { [P in keyof T] : { type: P } }>() : T{return  null;}
// Helper will be null at runtime 
let mapHelper = typeMapValidator<{
    "TypeA": TypeA
    "TypeB": TypeB
    "TypeC": TypeA // Will be an error
}>()
type ControlPageTypeMap = typeof mapHelper;
type ControlPageType = keyof ControlPageTypeMap;
export function findCurrentPage<K extends ControlPageType>(state: ApplicationState, type: K): ControlPageTypeMap[K] {
    //...
    return null;
};