首页 > 解决方案 > 解构歧视联合

问题描述

我正在尝试编写可与具有type属性的通用可区分联合体一起使用的代码。

假设我有一堆受歧视的工会,例如:

interface IFoo {
  type: "foo";
  foo: number;
}

interface IBar {
  type: "bar";
  bar: number;
}

interface IBaz {
  type: "baz";
  baz: number;
}

type IObject = IFoo | IBar | IBaz;

我解决的第一个任务是确定类型属性的可能值:

declare let _object: IObject;
type ObjectType = typeof _object.type;

(顺便说一句,有没有办法在没有额外声明的情况下做同样的事情?)

我需要声明一个要使用的泛型类型,例如:

Case<IObject, "foo"> // = IFoo
Case<IObject, "bar"> // = IBar

这样我就可以声明:

function filter<Type extends ObjectType>(
    objects: IObject[], 
    type: Type,
): Case<IObject, type>[] {
  return objects.filter((o) => o.type == type);
}

这可能吗?

标签: typescript

解决方案


是的,这是可能的

interface IFoo {
  type: "foo";
  foo: number;
}

interface IBar {
  type: "bar";
  bar: number;
}

interface IBaz {
  type: "baz";
  baz: number;
}

type IObject = IFoo | IBar | IBaz;

type TypeSwitch<N extends string, T extends { type: N }> =
       { [n in N]: T extends { type: n } ? T : never };

type Case<T extends { type: string }, N extends T['type']> =
       TypeSwitch<T['type'], T>[N];

type F = Case<IObject, "foo">; // = IFoo
type B = Case<IObject, "bar">; // = IBar

此外,您可以使用“索引类型查询”类型运算符来引用属性的类型(实际上与使用属性访问的语法相同[],只是它对类型进行操作)

type ObjectType = IObject['type'];

最后,使用上述所有filter内容可以按预期给出过滤后的数组元素的类型:

function filter<Type extends ObjectType>(
    objects: IObject[], 
    type: Type,
): Case<IObject, Type>[] {
  return objects.filter((o) => o.type == type);
}


let o: IObject[];
const a = filter(o, 'bar'); // inferred as const a: IBar[]

推荐阅读