typescript 键入一个管道方法,该方法重复地破坏属性并添加新属性

z4bn682m  于 2023-01-27  发布在  TypeScript
关注(0)|答案(1)|浏览(105)

我有一个带有字典的类,可以用管道函数填充字典,管道函数的形式如下:接受一个对象(字典),解构它的属性以用作参数,并返回一个具有新属性的对象(然后可以将新属性添加到字典中)。
是否有任何方法可以键入管道函数,以便只有先前添加的属性可以被反结构化?即:

type AugmentFun = (source: Record<string, any>) => Record<string, any>;

class Pipeable {
  constructor(private dict: Record<string, any>) {}

  static of = (dict: Record<string, any>) => new Pipeable(dict);
  static augment = (source: Pipeable, fun: AugmentFun) => {
    return Pipeable.of(Object.assign({}, source.dict, fun(source.dict)));
  };

  pipe = (...funs: AugmentFun[]) => funs.reduce(Pipeable.augment, this);
}

const p = new Pipeable({});

// This works & should not throw type errors
const res1 = p.pipe(
  () => ({ a: 1, b: 2 }),       // { a: 1, b: 2 }
  ({ a, b }) => ({ c: a + b }), // { a: 1, b: 2, c: 3}
  ({ c }) => ({ d: c ** 2 })    // { a: 1, b: 2, c: 3, d: 9}
);

// This should throw type errors but doesn't
const res2 = p.pipe(
  () => ({ a: 1, b: 2 }),       // { a: 1, b: 2 }
  ({ c }) => ({ d: c + 10 })    // Should be: "c" does not exist
);

(the augment()方法是纯方法,但它也可以改变原始示例,这并不太重要)

ntjbwcob

ntjbwcob1#

这是一个常见的问题;但遗憾的是,在TypeScript中对于未知数量的依赖函数的类型管道没有一流的支持。唯一的“* 非黑客 *”解决方案是使用重载。

class Pipeable {
  constructor(private dict: Record<string, any>) {}

  static of = (dict: Record<string, any>) => new Pipeable(dict);
  static augment = (source: Pipeable, fun: AugmentFun) => {
    return Pipeable.of(Object.assign({}, source.dict, fun(source.dict)));
  };

  pipe<A>(fn1: () => A): A
  pipe<A, B>(fn1: () => A, fn2: (arg: A) => B): B
  pipe<A, B, C>(fn1: () => A, fn2: (arg: A) => B, fn3: (arg: A & B) => C): C
  pipe<A, B, C, D>(
    fn1: () => A, 
    fn2: (arg: A) => B, 
    fn3: (arg: A & B) => C, 
    fn4: (arg: A & B & C) => D
  ): D
  pipe(...funs: AugmentFun[]){ 
    funs.reduce(Pipeable.augment, this) 
  }
}

这就要求我们拥有与我们想要支持的操作符数量相同的重载数量。由于缺乏替代方案,这种模式被用于RxJS中.pipe的类型化和其他库中的管道。

const p = new Pipeable({});

const res1 = p.pipe(
  () => ({ a: 1, b: 2 }),             // { a: 1, b: 2 }
  ({ a, b }) => ({ c: a + b }),       // { a: 1, b: 2, c: 3}
  ({ a, b, c }) => ({ d: c ** 2 })    // { a: 1, b: 2, c: 3, d: 9}
);

const res2 = p.pipe(
  () => ({ a: 1, b: 2 }),     
  ({ c }) => ({ d: c + 10 })  
//   ~ Property 'c' does not exist on type '{ a: number; b: number; }'
);

Playground

相关问题