使用'keyof扩展'功能注释时出现打字稿错误

时间:2018-12-01 20:26:32

标签: typescript

对于TS类型系统的各个方面还是新的。我收到一个'?' expected [1005]错误。

使用TS版本3.1.6

我的tsconfig编译器选项:

"compilerOptions": {
    "strict": true,
    "module": "commonjs",
    "outDir": "www/staging/js",
    "rootDir": "src/",
    "sourceMap": false,
    "target": "es5",
    "experimentalDecorators": true
  },

引起问题的代码是handler参数类型注释(特别是在_convert方法的右括号之前的位置):

private _convert(
  doc: any, 
  handler: keyof StringHelperService extends (str: string) => string
): any {
  let converted = {} as {[str: string]: any}

  for (let prop in doc) {
    converted[handler(prop)] = doc[prop];
  }
  return converted;
}

该批注应该说“ StringHelperService的键,仅当它具有以下功能签名时”。这是StringHelperService:

export class StringHelperService {
  public static $inject: ReadonlyArray<string> = [];


  constructor() {
    this.pascalToCamel = this.pascalToCamel.bind(this);
    this.camelToPascal = this.camelToPascal.bind(this);
  }


  pascalToCamel(str: string): string {
    return str.substring(0, 1).toLowerCase() + str.substring(1);
  }


  camelToPascal(str: string): string {
    return str.substring(0, 1).toUpperCase() + str.substring(1);
  }
}

我已经解决了这个问题。

这里是_convert的实现者,以帮助解释该方法的使用方式及其与StringHelperService的关系。我为函数声明创建了一个接口,如下所示:

export interface StringConverter {
  (str: string): string;
}

通过使用此界面代替helper的原始注释,我已经解决了该问题。问题是我的原始注释完全错误。我的谬误源于对以下误解的来源:我会使用key of类型来注释类型T的成员的类型,而实际上的确是我想要的方法。不知道为什么我会飞跃。

export class PropertyNameConverterService {
  public static $inject: ReadonlyArray<string> = ['stringHelper'];


  constructor(private stringHelper: StringHelperService) {
    this._convert = this._convert.bind(this);
    this.pascalToCamel = this.pascalToCamel.bind(this);
    this.camelToPascal = this.camelToPascal.bind(this);
  }


  private _convert(
    doc: any, 
    handler: StringConverter
  ): any {
    let converted = {} as {[str: string]: any}

    for (let prop in doc) {
      converted[handler(prop)] = doc[prop];
    }
    return converted;
  }


  pascalToCamel(doc: any): any {
    return this._convert(doc, this.stringHelper.pascalToCamel);
  }


  camelToPascal(doc: any): any {
    return this._convert(doc, this.stringHelper.camelToPascal);
  }
}

1 个答案:

答案 0 :(得分:2)

  

该批注应该是“ StringHelperService的键,只有它具有以下功能签名”。

这就是您要注释执行的操作。

type KeysWithThisSignature<T, U> = {
    [P in keyof T]: T[P] extends U ? P : never;
}[keyof T]

// type Keys = "pascalToCamel" | "camelToPascal"
type Keys = KeysWithThisSignature<StringHelperService, (str: string) => string>;

但是,您给出的示例是一个不同的用例。在示例中,handler不是string,而是具有特定签名的函数。

pascalToCamel(doc: any): any {
    // the second argument is a function not a string
    return this._convert(doc, this.stringHelper.pascalToCamel);
}

在这种情况下,您真正​​想要的_convert是:

private _convert(doc: any, handler: (str: string) => string): any {
    // ...
}