【问题标题】:Typescript: Change function argument type打字稿:更改函数参数类型
【发布时间】:2019-03-20 16:47:22
【问题描述】:

我正在设计一个类似于 Hystrix 的可靠性 API,并希望它能够像这样工作:

const target = request-promise;
const dispatcher = new Dispatcher(target)
  .fallback(...)
  .circuitBreaker()
  .throttle()
  .end();

// The new function has same signature as the target, 
// except that first argument becomes an array.
// It finally calls:
// request-promise('http://a.com') 
// or 
// request-promise('http://b.com')
dispatcher(['http://a.com', 'http://b.com'])
  .then(...);

现在我有问题定义类型以使其返回一个新函数,其第一个参数类型可以成为原始类型的数组。

它不起作用:

type TargetFn<T> = (...args: [T, ...any[]]) => Promise<any>;
type WrappedFn<F> = F extends TargetFn<infer T> ? TargetFn<T | T[]> : unknown;

class Dispatcher<F extends TargetFn> {

  constructor(private targetFn: F) {}

  end(): WrappedFn<T> {
    // ...
  }      
}

function chain<T, F extends TargetFn<T>>(fn: F): Dispatcher<T, F> {
  return new Dispatcher<T, F>(fn);
}

chain((url: string) => Promise.resolve(url)).end();
//    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

出现编译错误:

错误 TS2345:'(url: string) => Promise' 类型的参数不可分配给'TargetFn' 类型的参数。

  • “url”和“args_0”的参数类型不兼容。

    • 类型“{}”不可分配给类型“字符串”。

【问题讨论】:

    标签: typescript


    【解决方案1】:

    编译器有时会在推断相关的类型参数时遇到问题。

    我认为一种可行且实际上更简单的方法是使用条件类型来提取第一个参数类型:

    type TargetFn = (...args: any[]) => Promise<any>;
    type WrappedFn<F> = F extends (a: infer A1, ...args: infer U) => Promise<infer R> ? (a: A1|A1[], ...args: U) => Promise<R> : unknown;
    
    class Dispatcher<F extends TargetFn> {
    
    constructor(private targetFn: F) {}
    
        end(): WrappedFn<F> {
            return null as any;
        }      
    }
    
    function chain<F extends TargetFn>(fn: F): Dispatcher<F> {
        return new Dispatcher<F>(fn);
    }
    
    const o = chain((url: string) => Promise.resolve(url)).end(); 
    o("") // call it with a string
    o(["", ""]) // or an array
    

    注意更好的WrappedFn 版本实际上会返回一个具有多个重载的函数,而不是一个具有第一个参数A1|A1[] 的函数。每个重载都会根据需要返回RR[]

    type WrappedFn<F> = F extends (a: infer A1, ...args: infer U) => Promise<infer R> ? {
        (a: A1, ...args: U): Promise<R> 
        (a: A1[], ...args: U): Promise<R[]> 
    }: unknown;
    
    
    const o = chain((url: string) => Promise.resolve(url)).end(); 
    o("") // call it with a string, returns Promise<string>
    o(["", ""]) // or an array, returns Promise<string[]>
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2022-01-12
      • 1970-01-01
      • 2022-11-11
      • 2018-05-02
      • 2016-08-26
      • 2021-10-27
      • 2020-12-28
      • 2019-07-05
      相关资源
      最近更新 更多