Typescript 反射 - 必需的参数和默认值

Typescript reflection - required parameters & default values

简而言之:有没有办法知道是否需要打字稿参数and/or有默认值?

更长的版本: 假设我有以下文件:

//Foo.ts
class Bar {
    foo(required:string,defaultValue:number=0,optional?:boolean) {
        ...
    }
}

我想知道每个参数:

我已经成功地使用 method decorators with the TypeScript reflection API to get the types of the parameters, I've used this method 得到了他们的名字,但是到目前为止我还没有找到一种方法来知道一个变量是否是 required and/or有一个默认值

我知道 typescript 编译器本身可以在 typescript 中使用。所以想知道有没有办法利用编译器的parse tree来查看某个参数是否需要and/or有默认值?

那将如何运作?

如果您想从头开始...

在高层次上,一种方法是:

  1. 弄清楚如何使用文件的编译器 api 获取 SourceFile 节点。这本身就需要一些解释。

  2. 从那里,使用 api 的 forEachChild 函数遍历文件中的所有节点并找到具有 kind 的节点 SyntaxKind.ClassDeclaration 和 .name 属性 以及文本 Bar.

  3. 然后再次使用 api 的 forEachChild 函数遍历 class 的所有子节点,并得到类型为 SyntaxKind.MethodDeclaration 和 .name 属性 以及文本 foo.

  4. 获取参数需要循环方法节点的parameters 属性.

  5. 然后对于每个参数节点,要获取名称,您可以在 .name 属性.

    上调用 .getText()
  6. 您可以通过以下方式判断参数是否可选:

     const parameterDeclaration = parameterNode as ts.ParameterDeclaration;
     const isOptional = parameterDeclaration.questionToken != null || parameterDeclaration.initializer != null || parameterDeclaration.dotDotDotToken != null;
    

    或者您可以使用 TypeCheckerisOptionalParameter 方法。

  7. 要获取其默认表达式,您只需检查 initializer 属性:

     propertyDeclaration.initializer;
    
  8. 要获取类型,请使用 TypeCheckergetTypeOfSymbolAtLocation 方法并传入节点的符号...这有点复杂,所以我不会'懒得解释了(想想它与联合类型等有何不同)。

不要从头开始...

我已经围绕 TypeScript 编译器创建了一个包装器 api。只需将此代码与 ts-simple-ast (edit: Previously this talked about my old ts-type-info 库一起使用,但 ts-simple-ast 更好):

import { Project } from "ts-morph";

// read more about setup here:
// https://ts-morph.com/setup/adding-source-files
const project = new Project({ tsConfigFilePath: "tsconfig.json" });
const sourceFile = project.getSourceFileOrThrow("src/Foo.ts");
const method = sourceFile.getClassOrThrow("Bar").getInstanceMethodOrThrow("foo");

一旦你有了这个方法,就可以非常简单地从它的参数中获取你需要的所有信息:

console.log(method.getName()); // foo

for (const param of method.getParameters()) {
    console.log(param.getName());
    console.log(param.getType().getText());
    console.log(param.isOptional());
    console.log(param.getInitializer() != null);
}