简体   繁体   English

Typescript:我如何最明确地定义一个可以保存 function 名称或该函数返回类型的变量?

[英]Typescript: how do I most explicitly define a variable that can hold a function name or that function's return type?

I'm refactoring/rebuilding an existing codebase, and came across the equivalent of this code:我正在重构/重建现有的代码库,并遇到了这段代码的等价物:


class FeedbackBase {
    Variables = [{Value: 'doSomething'}];

    goToFeedback() {

        this.Variables?.forEach(variable => {
            if (variable.Value) {
                variable.Value = (this[variable.Value])();
            }
        });
    }

    doSomething(): string { return '123';}
}

The basic idea is that Variables[].Value might contain a function call defined on FeedbackBase .基本思想是Variables[].Value可能包含在FeedbackBase上定义的 function 调用。 If it does, then run the function, and replace Variables[].Value with the return value.如果是,则运行 function,并将Variables[].Value替换为返回值。

Before this.Variables?.forEach , they were originally doing a deep copy of this.Variables , wiping out all of the typing and basically running the code on an 'any' object. When we keep the typing, Typescript complains about this[variable.Value] :this.Variables?.forEach之前,他们最初是在做this.Variables的深度复制,消除所有输入并基本上在“任何”object 上运行代码。当我们继续输入时,Typescript 抱怨this[variable.Value]

Element implicitly has an 'any' type because expression of type 'string' can't be used to index type 'FeedbackBase'. No index signature with a parameter of type 'string' was found on type 'FeedbackBase'.

Fair enough: all Typescript knows is that the contents of variable.Value are a string : it doesn't know whether it is a callable function on FeedbackBase .很公平:Typescript 只知道variable.Value的内容是一个string :它不知道它是否是FeedbackBase上的可调用 function。

My potential solution:我的潜在解决方案:

type BaseFunction = {
    doSomething(): string;
};

class FeedbackBase implements BaseFunction {
    Variables = [{Value: 'doSomething'}];

    goToFeedback() {
        this.Variables?.forEach(variable => {
            if (variable.Value) {
                variable.Value = this[variable.Value as keyof BaseFunction]();
            }
        })
    }

    doSomething(): string { return '123';}
}

Basically:基本上:

  1. Within BaseFunction , explicitly define all of the functions that could be defined in variable.ValueBaseFunction中,显式定义所有可以在variable.Value中定义的函数
  2. FeedbackBase implements BaseFunction
  3. Call the function as this[variable.Value as keyof BaseFunction] to tell Typescript that this should be treated as a function call, not as a string.调用 function as this[variable.Value as keyof BaseFunction]告诉 Typescript 这应该被视为 function 调用,而不是字符串。

A nice feature of this approach is that when you say this[variable.Value as keyof BaseFunction] , Typescript checks to make sure that this indeed has all of the functions defined in BaseFunction .这种方法的一个很好的特点是,当你说this[variable.Value as keyof BaseFunction]时,Typescript 会检查以确保this确实具有BaseFunction中定义的所有函数。 (For example, if you change FeedbackBase.doSomething to FeedbackBase.doSomethings , an error appears at this[variable.Value as keyof BaseFunction] ). (例如,如果您将FeedbackBase.doSomething更改为FeedbackBase.doSomethings ,则会在this[variable.Value as keyof BaseFunction]处出现错误)。

I think that, in this case, I'm forced to use as , because variable.Value could be keyof BaseFunction , or whatever is returned by the function call.我认为,在这种情况下,我不得不使用as ,因为variable.Value可能是keyof BaseFunction ,或者 function 调用返回的任何内容。 Even if I get more specific about the return type, I can't think of a way to define variable.Value as definitely a function call before the transformation, and definitely a return type afterward, outside of just using as .即使我对返回类型有更具体的了解,我也想不出一种方法来定义variable.Value在转换之前绝对是一个 function 调用,并且在转换之后绝对是一个返回类型,除了使用as之外。

Is there a way to be more explicit?有没有办法更明确?

I don't know if this is the best TypeScript per say because it seems a bit convoluted, but I think your solution is fine in terms of typings.我不知道这是否是最好的 TypeScript 因为它看起来有点复杂,但我认为你的解决方案在打字方面很好。 I would beware of one thing though.我会提防一件事。

I don't think the forEach does what you want it to.我不认为forEach会做你想做的事。 If a Value is not a function, you'll get a runtime error because you're not actually checking if Value is a function. Instead, you're checking if it exists.如果Value不是 function,您将收到运行时错误,因为您实际上并未检查Value是否为 function。相反,您正在检查它是否存在。

this.Variables?.forEach((variable) => {
      if (
        variable.Value &&
        variable.Value in this &&
        typeof this[variable.Value as keyof BaseFunction] === "function"
      ) {
        variable.Value = this[variable.Value as keyof BaseFunction]()
      }
})

TS would not allow you to do that if it knew what you wanted to do, and I think it would be right in complaining.如果 TS 知道你想做什么,它不会允许你这样做,我认为抱怨是正确的。 This is what this answer will attempt to demonstrate.这就是这个答案将试图证明的。

You don't need an interface to wire the types.您不需要接口来连接类型。 You can statically work out the names of FeedbackBase non-void method names like so:您可以像这样静态地计算出FeedbackBase非空方法名称的名称:

type NonVoidMethodNames<Class> = keyof {
    [K in keyof Class
        as ((...args: any[]) => void) extends Class[K] ? never
        : Class[K] extends (...args: any[]) => unknown ? K
        : never
    ]: K
}

type DoSomething = NonVoidMethodNames<FeedbackBase> // "doSomething"

If you assigned it to Variables like the following, you would not need your as when you call this[variable.Value]() .如果您将它分配给如下所示的Variables ,则在调用this[variable.Value]()时不需要您的as

Variables: { Value: NonVoidMethodNames<FeedbackBase> }[] = [{ Value: 'doSomething' }];

TS would try to index this with "doSomething" , realise it's a method, no problem, and work out the return type as string as expected TS 会尝试用"doSomething"索引this ,意识到这是一个方法,没问题,并按预期将返回类型计算为string

But of course we can't simply do that because now we're assigning string to variable.Value which we had defined to be "doSomething" .但当然我们不能简单地这样做,因为现在我们将string分配给variable.Value我们定义为"doSomething"

playground 操场

In TS, you can't write the following and this is the situation you are in, only with indirections in-between.在 TS 中,您不能编写以下内容,这就是您所处的情况,只能在两者之间进行间接访问。

let foo: string = 'a';
foo = 1;

You would also be inconvenienced if you wanted to widen Value to MethodName | ReturnType如果您想将Value扩大到MethodName | ReturnType ,您也会感到不便。 MethodName | ReturnType when the return type is string as it is the case here because string would swallow its subtypes. MethodName | ReturnType当返回类型是string时,就像这里的情况一样,因为string会吞下它的子类型。

Even ignoring the problem of when Variables is populated, the user has no way to know what values this field contains.即使忽略Variables何时填充的问题,用户也无法知道该字段包含什么值。 This is a deal breaker in my opinion.在我看来,这是一个交易破坏者。 I am not very savvy in OO but I would say this design is flawed and cannot be typed.我对 OO 不是很了解,但我会说这种设计有缺陷,无法输入。

暂无
暂无

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

相关问题 如何在Typescript接口中为函数定义void的返回类型? - How can I define a return type of void for a function in a Typescript interface? 在TypeScript中,如何键入函数的参数而不是返回值? - In TypeScript, how do i type a function's arguments but not the return value? TypeScript:对于这个 function,如何将类型显式传递给 generics - TypeScript: How can I explicitly pass in the type to the generics for this function 在TypeScript中,如何根据其参数之一的返回类型来确定函数的返回类型? - In TypeScript, how do I make a function's return type based on the return type of one of its arguments? 如何显式注释 TypeScript 混合工厂函数的返回类型? - How to explicitly annotate the return type of a TypeScript mixin factory function? 如何为函数定义 Typescript 对象返回值? - How can I define a Typescript object return value for a function? 我可以定义一个枚举和函数的打字稿变量吗? - Can I define a typescript variable that is an enum and a function? TypeScript:你能根据函数的参数定义一个返回类型结构吗? - TypeScript: Can you define a return Type structure based on an argument of the function? 无法在 typescript 上正确定义 function 返回类型 - can't define function return type properly on typescript 当我在 Typescript 中传递给函数时,如何返回相同的类型? - How can I return the same type as I pass into a function in Typescript?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM