当表达式用作具有参数类型的参数的acess函数的表达式具有可选字段时,TypeScript不会编译。 TS2349

时间:2016-12-21 11:46:12

标签: typescript tsc

以下代码无法在TypeScript 2.1.4中编译,并给出错误:

错误

  

错误:(6,31)TS2349:无法调用类型缺少调用签名的表达式。输入'((args:string [],action:A)=> string [])| ((args:string [],action:C)=> string [])'没有兼容的呼叫签名。

代码

/*
* Set up a function to take some arguments
* and an action and use the map to run the appropriate
* function bases on the type property of the action 
*/ 

const caller = (args: string[] = [], action): string[] => {
        return map[action.type] ? map[action.type](args, action) : args;
 };

interface Action {
    type: any; 
}

const TYPE_A = "type_a";

interface A extends Action {
    from: number;
    to: number;
    id?: number; // optional parameters causing the issue.
    prop1?: number;
}

const TYPE_B = "type_b";

interface B extends Action {
    from: number;
    to: number;
}

const TYPE_C = "type_c";

interface C extends Action {
    id: number;
    prop1: number;
}

const map = {
    [TYPE_A]: (args: string[], action: A) => {
        return ["a"];
    },
    [TYPE_B]: (args: string[], action: B) => {
        return ["b"];
    },
    [TYPE_C]: (args: string[], action: C) => {
        return ["c"];
    }
};

caller([], {type: TYPE_A, from: 2, to: 1});

动机

我将表达式用作地图中的属性的动机是,我可以更改属性常量的值,而无需重构地图。

解决方案

有两种方法可以解决这个问题:

a)删除interface A中的可选字段。

interface A extends Action {
    from: number;
    to: number;
    id: number; // optional parameters causing the issue not optional.
    prop1: number;
}

b)将地图属性声明更改为值而不是表达式并保留可选字段。

const map = {
    "type_a" : (args: string[], action: A) => {
        return ["a"];
    },
    "type_b": (args: string[], action: B) => {
        return ["b"];
    },
    "type_c": (args: string[], action: C) => {
        return ["c"];
    }
};

问题

我的问题是为什么首先显示错误,有人可以向我解释这个吗?

1 个答案:

答案 0 :(得分:0)

原因是AC不兼容,因为prop1中的A是可选的,C中是必需的。因此,您不能使用需要C的函数,其中需要A的函数:

let fa: (a: A) => void;
let fc: (c: C) => void;

fa = fc;
fc = fa;

错误:

test.ts(49,1): error TS2322: Type '(c: C) => void' is not assignable to type '(a: A) => void'.
  Types of parameters 'c' and 'a' are incompatible.
    Type 'A' is not assignable to type 'C'.
      Property 'id' is optional in type 'A' but required in type 'C'.
test.ts(50,1): error TS2322: Type '(a: A) => void' is not assignable to type '(c: C) => void'.
  Types of parameters 'a' and 'c' are incompatible.
    Type 'C' is not assignable to type 'A'.
      Property 'from' is missing in type 'C'.

当您声明具有文字属性名称的地图时,类型推断可以确定何时执行caller([], {type: TYPE_A, from: 2, to: 1});,您实际上是使用"type_a"键访问值,因此它知道函数参数类型正是A。当使用计算键声明map时,它不能这样做,可能是因为它只是在编译时不计算键的表达式,因此它推断出映射值的联合类型,并且union的两个成员彼此不兼容,因为AC不兼容。

您也可以通过明确声明map的类型来解决这个问题:

const map: {[key: string]: (args:string[], action: Action) => string[]} = {
    [TYPE_A]: (args: string[], action: A) => {
        return ["a"];
    },
    [TYPE_B]: (args: string[], action: B) => {
        return ["b"];
    },
    [TYPE_C]: (args: string[], action: C) => {
        return ["c"];
    }
};

也有效。