【问题标题】:TypeScript function parameter whose parameters can only be simple typesTypeScript 函数参数,其参数只能是简单类型
【发布时间】:2021-12-03 21:51:17
【问题描述】:

我很想在 typescript 中创建一个 memoize 函数,并且应该能够被 memoized 的函数必须只接受简单类型。

type ArgTypes<T> = T extends (...a: infer A) => unknown ? A : [];
type FuncWithSimpleParams = (...args: (number | string | boolean | null | undefined)[]) 
=> any;

export function memoize<T extends FuncWithSimpleParams>(func: T) {
  const cache = {}

  return function wrapper(...args: ArgTypes<T>) {
    const cacheKey = args.join('.');
    const cacheHit = cache.get(cacheKey);
    if (cacheHit) {
      return cacheHit;
    }
    const result = func(...args);
    cache[cacheKey] = result;
    return result;
  };
}

但是当我这样使用它时

memoize(function (first: string, second: string) {
  return `${first}${second}`
})  

我收到打字稿错误提示

参数“first”和“args”的类型不兼容。 键入'字符串 |号码 |布尔值 |空 | undefined' 不能分配给类型 'string'。

【问题讨论】:

  • 与问题无关,可以使用内置Parameters代替ArgTypes来获取函数参数

标签: typescript


【解决方案1】:

这个函数类型:

type FuncWithSimpleParams = (...args: (number | string | boolean | null | undefined)[])
=> any;

声明一个可以使用任何这些参数调用的函数。但是你传给它一个只能接受string 参数的函数。

“但是,Alex”,你是说“T extends FuncWithSimpleParams 应该允许这样做,对吧?”

嗯,不。

type Test = // This is `false`
     ((a: 1) => void) extends ((a: 1 | 2) => void)
          ? true
          : false

所以函数参数不能以这种方式扩展。


您确实有错误的泛型。相反,您希望将函数的参数作为泛型类型,这样您就不必担心扩展函数类型的复杂语义。

例如:

type SimpleType = number | string | boolean | null | undefined
type FuncWithSimpleParams<T extends SimpleType> = (...args: T[]) => any;

现在FuncWithSimpleParams 有一个泛型。这意味着结果将只允许 SimpleType 的子集作为参数,无论是通用参数的子集。

现在memoize 变为:

export function memoize<T extends SimpleType>(func: FuncWithSimpleParams<T>) {

以及所有works as expected on this playground


此外,现在您完全摆脱了ArgTypes(无论如何只是Parameters&lt;T&gt;),而只需使用T[],因为您知道参数类型而无需显式询问函数。

export function memoize<T extends SimpleType>(func: FuncWithSimpleParams<T>) {
  const cache = {}

  return function wrapper(...args: T[]) { // just T[] now
    const cacheKey = args.join('.');
    const cacheHit = cache.get(cacheKey);
    if (cacheHit) {
      return cacheHit;
    }
    const result = func(...args);
    cache[cacheKey] = result;
    return result;
  };
}

当你得到正确的答案时,一切似乎都变得简单了,真是令人惊讶。


这几乎可行。我的示例应该使用不同类型的参数。如果将“第二个”参数更改为数字。你得到了我所指的错误

如果你想允许任意数量的不同类型的参数,那么你的泛型需要是一个数组类型,这允许它把参数推断为一个已知长度的元组类型,其中每个索引都有一个特定的类型。

type SimpleType = number | string | boolean | null | undefined

type FuncWithSimpleParams<T extends SimpleType[]> = (...args: T) 
=> any;

export function memoize<T extends SimpleType[]>(func: FuncWithSimpleParams<T>) {
  const cache = {}

  return function wrapper(...args: T) {
    const cacheKey = args.join('.');
    const cacheHit = cache.get(cacheKey);
    if (cacheHit) {
      return cacheHit;
    }
    const result = func(...args);
    cache[cacheKey] = result;
    return result;
  };
}

memoize(function (first: string, second: number) {
  return `${first}${second}`
})

Playground

【讨论】:

  • 这几乎可以工作。我的示例应该使用不同类型的参数。如果将“第二个”参数更改为数字。你得到了我所指的错误。
  • 我已经更新了我的答案。
猜你喜欢
  • 2023-02-22
  • 2013-11-03
  • 2021-05-29
  • 2021-07-08
  • 2016-11-01
  • 2018-08-28
  • 2020-04-27
  • 2012-09-23
  • 1970-01-01
相关资源
最近更新 更多