TypeChecker API:如何查找函数的推断类型参数?

Sim*_*lis 7 typescript typescript-compiler-api

给定具有推断类型参数的 CallExpression,有什么方法可以找到这些类型参数是什么?

示例代码:

class SomeClass {
    public someMethod<T>(arg: T): void { }
}

// What is the inferred type of T in this call?
someClass.someMethod(7);
Run Code Online (Sandbox Code Playgroud)

找到代码中显式分配的类型参数很容易,但我不知道如何找到推断的内容。

function inferTypeArguments(node: ts.CallExpression, typeChecker: ts.TypeChecker) {
    node.typeArguments; // is empty
    const signature = typeChecker.getResolvedSignature(node);
    signature['typeArguments']; // is also empty

    // This returns "<number>(arg: number): void"
    // so I know that the typeChecker has the right information,
    // but I would really like a ts.Type[]
    typeChecker.signatureToString(signature, node, ts.TypeFormatFlags.WriteTypeArgumentsOfSignature)
}
Run Code Online (Sandbox Code Playgroud)

Mei*_*hes 1

我在我的转译器中使用 Simon 的代码有一段时间了……然后 3.9 的出现打破了它。我已经做了初步尝试让它再次运行。不幸的是,映射器是打字稿的“内部”关注点,所以这在未来可能会再次改变

/* @internal - from typescript 3.9 codebase*/
const enum TypeMapKind {
  Simple,
  Array,
  Function,
  Composite,
  Merged,
}

/* @internal - from typescript 3.9 codebase*/
type TypeMapper =
  | { kind: TypeMapKind.Simple, source: ts.Type, target: ts.Type }
  | { kind: TypeMapKind.Array, sources: readonly ts.Type[], targets: readonly ts.Type[] | undefined }
  | { kind: TypeMapKind.Function, func: (t: ts.Type) => ts.Type }
  | { kind: TypeMapKind.Composite | TypeMapKind.Merged, mapper1: TypeMapper, mapper2: TypeMapper };

/* basic application of the mapper - recursive for composite.*/ 
function typeMapper(mapper:TypeMapper, source: ts.Type): ts.Type {
  switch(mapper.kind){
    case TypeMapKind.Simple: 
      return mapper.target;
    case TypeMapKind.Array:
      throw Error("not implemented");
    case TypeMapKind.Function: 
      return mapper.func(source);      
    case TypeMapKind.Composite: 
    case TypeMapKind.Merged:
      return typeMapper(mapper.mapper2, source);
  }
}

function inferTypeArguments(node: ts.CallExpression, typeChecker: ts.TypeChecker): ts.Type[] {
  const signature:ts.Signature = typeChecker.getResolvedSignature(node);
  const targetParams: ts.TypeParameter[] = signature['target'] && signature['target'].typeParameters;

  if (!targetParams) {
    return [];
  }

  if(signature['mapper'] == undefined)   
    return targetParams;   

  //typescript <= 3.8
  if(typeof signature['mapper'] == "function") 
    return targetParams.map(p=>signature['mapper'](p));
  //typescript >= 3.9.... 
  return targetParams.map(p=> typeMapper(signature['mapper'] as TypeMapper, p));
} 
Run Code Online (Sandbox Code Playgroud)