基于字段的TypeScript泛型推断

基于字段的TypeScript泛型推断,typescript,typescript-typings,typescript-generics,Typescript,Typescript Typings,Typescript Generics,我想声明一个类型问题,它依赖于他的类型键,将在oldEntity和newEntity中返回不同的值。 我想在不需要手动转换对象的情况下使用开关运行myFunc,TypeScript将根据type属性推断类型。但效果并不理想。 如何使开关自动推断类型而无需转换。 有没有更好的方法来声明不同类型的问题,以便开关可以推断它 这里有一个链接到请注意,诸如Letter.a | Letter.B之类的类型也扩展了Letter,因此编译器不知道事情是否像现在这样受到约束 类似这样的方法会奏效: 枚举字母{ A

我想声明一个类型问题,它依赖于他的类型键,将在oldEntity和newEntity中返回不同的值。 我想在不需要手动转换对象的情况下使用开关运行myFunc,TypeScript将根据type属性推断类型。但效果并不理想。 如何使开关自动推断类型而无需转换。 有没有更好的方法来声明不同类型的问题,以便开关可以推断它

这里有一个链接到

请注意,诸如Letter.a | Letter.B之类的类型也扩展了Letter,因此编译器不知道事情是否像现在这样受到约束

类似这样的方法会奏效:

枚举字母{ A=A, B=B, C=C } 导出类型实体= T扩展字母A?{a:number} :T扩展字母B?{b:string} :T扩展字母C?{c:string} :从不; 导出类型DeltaT=K扩展了EntityT的键 ? { 关键词:K; oldValue:EntityT[K]; newValue:EntityT[K]; } :从不; 导出类型问题t={ id:字符串; 类型:T; delta:DeltaT[]; 旧实体:实体; 新实体:EntityT; }; 函数myFunce:ProblemT | ProblemT | ProblemT{ 开关e.type{ 案例信函A:返回e.newEntity.A; 案例信函.B:返回e.newEntity.B; 案例信函.C:返回e.newEntity.C; } } 请注意,诸如Letter.A | Letter.B之类的类型也扩展了Letter,因此编译器不知道事情是否像它们那样受到约束

类似这样的方法会奏效:

枚举字母{ A=A, B=B, C=C } 导出类型实体= T扩展字母A?{a:number} :T扩展字母B?{b:string} :T扩展字母C?{c:string} :从不; 导出类型DeltaT=K扩展了EntityT的键 ? { 关键词:K; oldValue:EntityT[K]; newValue:EntityT[K]; } :从不; 导出类型问题t={ id:字符串; 类型:T; delta:DeltaT[]; 旧实体:实体; 新实体:EntityT; }; 函数myFunce:ProblemT | ProblemT | ProblemT{ 开关e.type{ 案例信函A:返回e.newEntity.A; 案例信函.B:返回e.newEntity.B; 案例信函.C:返回e.newEntity.C; } }
enum Letter {
  A = "A",
  B = "B",
  C = "C"
}

export type EntityT<T extends Letter = Letter> = T extends Letter.A
  ? { a: number }
  : T extends Letter.B
  ? { b: string }
  : T extends Letter.C
  ? { c: string }
  : never;

type DeltaT<T extends EntityT> = _DeltaT<T, keyof T>;

export type _DeltaT<E extends EntityT, K extends keyof E> = {
  key: K;
  oldValue: E[K];
  newValue: E[K];
};

export type ProblemT<T extends Letter = Letter> = EntityT<T> extends infer E
  ? {
      id: string;
      type: T;
      delta: DeltaT<E>[];
      oldEntity: E;
      newEntity: E;
    }
  : never;

const testCase: ProblemT<Letter.A> = {
  id: "id",
  type: Letter.A,
  delta: [{ key: "a", oldValue: 1, newValue: 2 }],
  oldEntity: { a: 1 },
  newEntity: { a: 2 }
};

function myFunc<T extends Letter>(e: ProblemT<T>) {
  switch (e.type) {
    case Letter.A: {
      const uselessConversion = e as ProblemT<Letter.A>;
      return uselessConversion.newEntity.a;
    }
    case Letter.B: {
      const uselessConversion = e as ProblemT<Letter.B>;
      return uselessConversion.newEntity.b;
    }
    case Letter.C: {
      const uselessConversion = e as ProblemT<Letter.C>;
      return uselessConversion.newEntity.c;
    }
  }
}