类型不兼容错误,没有明显的不兼容

时间:2018-07-23 10:38:32

标签: typescript

我有一个“选项”元组[string,function],该元组提供一个string来标识一个检查,以及一个function,将在其上执行该检查。 (无法通过功能分析找出执行检查的方法,因此必须通过该字符串指示实际检查。) 在我为实现此目的而编写的代码中,在我看来,检查的类型和转换函数的类型不会错配。 但是TypeScript不赞成,给了我一个不兼容的错误。

// tsconfig.json
// {"compilerOptions": {"noImplicitAny": true}};

// Types and interfaces

interface f1_sig { (x:string): string }
interface f2_sig { (x:number): number }

interface f1Chk_sig { (f1:f1_sig): boolean }
interface f2Chk_sig { (f2:f2_sig): boolean }

type options_type = 
  ['f1',f1_sig] | 
  ['f2',f2_sig] ;

interface optionsDictionary_indexer {
  f1: f1Chk_sig;
  f2: f2Chk_sig;
}

// Implementations

const f1: f1_sig = x => x;
const f2: f2_sig = x => x;
const f1Chk: f1Chk_sig = f => f('0')?true:false;
const f2Chk: f2Chk_sig = f => f( 0 )?true:false;

const optionsDictionary: optionsDictionary_indexer = {
  f1: f1Chk,
  f2: f2Chk
};

// Alternative 1
const optionsExtractor_1:
  (options: options_type) => boolean
  =
options => 
  optionsDictionary[options[0]](options[1])
  // Error in the text editor: 'f1Chk | f2Chk' has no compatible call signatures

// Alternative 2
const optionsExtractor_2:
  (options: options_type) => boolean
  =
options =>
  options[0]==='f1' ? f1Chk(options[1]) :
  options[0]==='f2' ? f2Chk(options[1]) : false;
  // Error in the text editor: Argument of type 'f1Chk | f2Chk' is not assignable to parameter of type f2_sig

如何混合类型?我哪里错了?如果我没记错的话,有人能提出一种轻松的方法来减轻TypeScript的恐惧吗,因为担心f1Chkf2调用或f2Chkf1调用?

1 个答案:

答案 0 :(得分:3)

这里有两件事。

您的“替代1”遇到了一个问题,我称之为“相关类型”,其中您有一个表达式在联合类型上无效,即使该表达式对每个联合类型均有效该联盟的可能范围缩小。在TypeScript中,目前还没有很好的解决方案。我已经suggested允许您强迫编译器解决可能的缩小问题,但实现的可能性很小。也许总有一天,控制流分析将足够复杂,可以正常工作。但就目前而言,我所知道的唯一解决方法是类型断言或冗余代码。

您的“替代2”是我正在谈论的冗余代码。但这仍然行不通!这是由于outstanding bug导致,当您对access the property使用括号表示法时,对属性的控制流分析不起作用。如果您使用点表示法访问元组属性,则代码不会出错...但是不幸的是,没有办法对数字索引使用点表示法。您可以将代码更改为使用某些内容而不是元组,例如:

type options_type =
  {zero: 'f1', one: f1_sig} |
  {zero: 'f2', one: f2_sig};

// Alternative 2, works now
const optionsExtractor_2:
  (options: options_type) => boolean
  =
  options =>
    options.zero === 'f1' ? f1Chk(options.one) :
      options.zero === 'f2' ? f2Chk(options.one) : false;

但是您可能应该只使用类型断言,这是比编译器更聪明时的推荐方法:

interface fEitherChk_sig { (f12: f1_sig | f2_sig): boolean }
// Alternative 1
const optionsExtractor_1:
  (options: options_type) => boolean
  =
  options =>
    (optionsDictionary[options[0]] as fEitherChk_sig)(options[1])

希望有帮助。祝你好运!