首页 > 解决方案 > TypeScript:难以键入可以在对象内部的特定级别进行过滤同时保留其原始结构的函数

问题描述

我有一个名为的对象myCached,它的类型被称为

interface MyCache {
  a: {
    b: {
      c: Target[];
      d: string;
    };
  };
}

Target项目在特定级别的属性之一中的类型在哪里,这种类型Target也是已知的。假设Target是这样的

type Target = "toRemove1" | "toRemove2" | "toPreserve1" | "toPreserve2";

在这里的例子中,myCache

const cache: MyCache = {
  a: {
    b: { 
      c: ['toRemove1', 'toPreserve1', 'toRemove2', 'toPreserve2'], 
      d: "irrelevant" 
    },
  }
}

我有一系列需要删除的项目。它们也有如下Target类型

const thingsToRemove: Target[] = ["toRemove1", "toRemove2"];

我正在尝试提出一个可以遍历这个对象并过滤掉特定级别的项目的函数。我设计这个函数的方式是它接受一个transformer对象并遍历该对象并提供过滤功能。我想正确键入它,以便此函数的用户可以回复 TS 编译器提供的自动完成功能并进行类型检查以确保此函数正确落在正确的级别上,在本例中是Target级别。

这是我的代码


type MappedTransform<T> = {
  [K in keyof T]?: MappedTransform<T[K]> | ((params: T[K]) => T[K]);
};

type Entries<T> = { [K in keyof T]-?: [K, T[K]] }[keyof T];

function traverse<R>(cache: R, transformObject: MappedTransform<R>): R {
  return (Object.entries(transformObject) as Array<
    Entries<MappedTransform<R>>
  >).reduce(reduceTransformNode, cache);
}

const reduceTransformNode = <R, K extends keyof R>(
  cacheNode: R,
  [transformKey, transformValue]: [
    K,
    MappedTransform<R[K]> | ((params: R[K]) => R[K]) | undefined
  ]
): R => {
  const { [transformKey]: node } = cacheNode;

  if (typeof transformValue === "undefined") return cacheNode;

  const newCacheValue =
    typeof transformValue === "function"
      ? (transformValue as (params: R[K]) => R[K])(node)
      : traverse(transformValue as R[K], node);

  return {
    ...cacheNode,
    [transformKey]: newCacheValue
  };
};

const x = traverse(cache, {
  a: {
    b: {
      // Need to use Type `Target` to make sure that the transformer function lands on the type of `Target` exactly
      c: (node) => node.filter((s) => !thingsToRemove.includes(s))
    }
  }
});

这工作正常,我已经非常接近我想要实现的目标,除了我似乎无法找到添加类型的方法Target以确保用户提供的用于执行过滤的功能实际上处于正确的级别,node即正确的类型Target

这是现场演示。有人能告诉我如何实现我在这里寻求的最后一点类型安全吗?

标签: javascripttypescript

解决方案


推荐阅读