简体   繁体   English

打字稿::条件链接函数

[英]Typescript :: Conditional chaining function

I'm trying to implement a set of chained function but somehow I get stuck here.我正在尝试实现一组链式函数,但不知何故我卡在这里了。

interface ISimpleCalculator {
  plus(value: number): this;
  minus(value: number): this;
  divide(value: number): this;
  multiply(value: number): this;
  sum(): void
}

interface ISpecialCalculator extends ISimpleCalculator {
  specialPlus(value: number): ISimpleCalculator;
  specialMinus(value: number): ISimpleCalculator;
}

let testCalculator: ISpecialCalculator;
testCalculator  
  .plus(20)
  .multiply(2)
  .specialPlus(40)  
  .plus(20)
  .minus(5)
  .specialMinus(20)  //<-- Error! Property 'specialMinus' does not exist on type 'ISimpleCalculator'.
  .sum()

I want to archive type check of the function in the chain.我想归档链中函数的类型检查。 In the above example, I want the functions specialPlus and specialMinus in ISpecialCalculator to be used once only and ISimpleCalculator can be used for multiple times.在上述例子中,我想要的功能specialPlusspecialMinusISpecialCalculator进行一次仅使用和ISimpleCalculator可用于多次。 I'm pretty fresh to the typescript and I've been trying different approaches (Advanced type ( Pick & Omit )) with no success so far.我对打字稿很陌生,到目前为止我一直在尝试不同的方法(高级类型( Pick & Omit ))但没有成功。 I want to know is there any other way to help in this case.我想知道在这种情况下有没有其他方法可以提供帮助。

Removing the some functions is simple, you can just use Omit<this, 'specialPlus'> If we test this it almost works, if you call specialPlus you will get an error if you call it immediately after another call to specialPlus , you can however call it after a call to specialMinus删除一些函数很简单,你可以使用Omit<this, 'specialPlus'>如果我们测试它几乎可以工作,如果你调用specialPlus你会得到一个错误,如果你在再次调用specialPlus之后立即调用它,你可以在调用specialMinus之后调用它

interface ISpecialCalculator extends ISimpleCalculator {
  specialPlus(value: number): Omit<this, 'specialPlus'>;
  specialMinus(value: number): Omit<this, 'specialMinus'>;
}

declare let testCalculator: ISpecialCalculator;
testCalculator  
  .specialPlus(40)
   // .specialPlus(40) // error 🎉
  .specialMinus(20)
  .specialPlus(40) //ok 😢
  .sum()

Playground Link 游乐场链接

This is because Omit will work on the this type bound when testCalculator is declared, so specialMinus will return in fact Omit<ISpecialCalculator, 'specialMinus'> which will still contain specialPlus even though we previously removed it.这是因为Omit将在工作this类型时势必testCalculator声明,所以specialMinus实际上将返回Omit<ISpecialCalculator, 'specialMinus'>这仍将包含specialPlus即使我们以前删除它。 What we want is for Omit to work on the type of this returned by the previous function.我们想要的是让Omit处理前一个函数返回的this类型。 We can do this if we capture the actual type of this for each call using a generic type parameter, and Omit methods from this type parameter not from polymorphic this .我们可以做到这一点,如果我们捕获的实际类型的this使用泛型类型参数的每次通话,并Omit这种类型参数的方法不是从多态this

interface ISimpleCalculator {
  plus<TThis>(this: TThis,value: number): TThis;
  minus<TThis>(this: TThis,value: number): TThis;
  divide<TThis>(this: TThis,value: number): TThis;
  multiply<TThis>(this: TThis,value: number): TThis;
  sum(): void
}

interface ISpecialCalculator extends ISimpleCalculator {
  specialPlus<TThis>(this: TThis, value: number): Omit<TThis, 'specialPlus'>;
  specialMinus<TThis>(this: TThis, value: number): Omit<TThis, 'specialMinus'>;
}

declare let testCalculator: ISpecialCalculator;
testCalculator
  .specialPlus(40)
  // .specialPlus(40) // error 🎉
  .specialMinus(20)
  .plus(10)
  .specialPlus(40) // also error 🎉
  .plus(10)
  .sum()

Playground Link 游乐场链接

specialPlus(value: number): ISimpleCalculator;

When you call this function, you are getting back a simple calculator that doesn't have the special functions anymore.当您调用此函数时,您将返回一个不再具有特殊功能的简单计算器。 The special interface should also return this and it should be working:特殊接口也应该返回this并且它应该可以工作:

interface ISpecialCalculator extends ISimpleCalculator {  
   specialPlus(value: number): this;
   specialMinus(value: number): this;
}

Try the following (full code tested based on the question):尝试以下操作(基于问题测试的完整代码):

interface ISimpleCalculator {
  plus(value: number): this
  minus(value: number): this
  divide(value: number): this
  multiply(value: number): this
  sum(): void
}

interface ISpecialCalculator extends ISimpleCalculator {
  specialPlus(value: number): this
  specialMinus(value: number): this
}

let testCalculator: ISpecialCalculator
testCalculator
  .plus(20)
  .multiply(2)
  .specialPlus(40)
  .plus(20)
  .minus(5)
  .specialMinus(20) 
  .sum()

If you want to limit the special[Plus|Minus] usage then you can accomplish that in the concrete class that implements the ISpecialCalculator interface.如果您想限制特殊 [Plus|Minus] 的使用,那么您可以在实现ISpecialCalculator接口的具体类中实现。

The code below may give you some ideas:下面的代码可能会给你一些想法:

class Calculator implements ISpecialCalculator {
  specialPlusUsed = false
  specialMinusUsed = false

  specialPlus(value: number): this {
    if (this.specialPlusUsed) throw new Error("SpecialPlus can be used only once!")

    this.specialPlusUsed = true
    // Related calculations here...
    return this
  }

  specialMinus(value: number): this {
    if (this.specialMinusUsed) throw new Error("SpecialMinus can be used only once!")
    this.specialMinusUsed = true
    // Related calculations here...
    return this
  }

  plus(value: number): this {
    // Related calculations here...
    return this
  }

  minus(value: number): this {
    // Related calculations here...
    return this
  }

  divide(value: number): this {
    // Related calculations here...
    return this
  }

  multiply(value: number): this {
    // Related calculations here...
    return this
  }

  sum(): void {
    // Related calculations here...
  }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM