首页 > 解决方案 > 有没有办法根据 Typescript 中函数的另一个参数来缩小参数的类型?

问题描述

我正在尝试为一些过滤器类型的操作创建一个迷你 DSL,并为它编写一些辅助方法。

说我有功能

const equals = (left, right) => {}

需要键入此函数,以使left值是对象上的字段,并且right是该对象类型的值。

如果我这样做const equals = <T> (left: keyof T, right: T[keyof T]) => {},我会接近但right会缩小到可用的类型,T而不仅仅是left.

可以通过以下方式实现所需的行为:

const equals = <T, F extends keyof T>(left: F, right: T[F])

但这需要两个通用参数,这会破坏我围绕此函数的代码的类型推断链。理想情况下,我想根据第一个参数键入第二个参数。这可能吗?

谢谢

标签: typescript

解决方案


您可能正在寻找以下几行的内容。解决方案是将一些类型验证逻辑移至智能构造函数。

// model
type PickPropertiesOfType<T, A> = Pick<
  A,
  {
    [K in keyof A]: A[K] extends T ? K : never;
  }[keyof A]
>;

export type Filter<A> =
  | { kind: "Equals"; field: string & keyof A; value: A[string & keyof A] }
  | { kind: "EqualsField"; field: string & keyof A; value: string & keyof A };

export const equalsOtherField = <A, K extends string & keyof PickPropertiesOfType<A[K], A>>(
  field: string & K,
  value: string & keyof PickPropertiesOfType<A[K], A>
): Filter<A> => ({
  kind: "EqualsField",
  field,
  value: value,
});

export const equals = <A, K extends string & keyof A>(field: string & K, value: A[K]): Filter<A> => ({
  kind: "Equals",
  field,
  value: value,
});

// example
type ExampleEntity = {
    author: string;
    publisher: string;
    year: number;
}

const exampleEntity: ExampleEntity = {
    author: "Foo",
    publisher: "Foobar",
    year: 2000
}

const filterArr: Filter<ExampleEntity>[] = [
  equals("author", "Bar"),
  equalsOtherField("author", "publisher"),
];

推荐阅读