当表达式用作 属性 名称以使用类型具有可选字段的参数访问函数时,TypeScript 无法编译。 TS2349
TypeScript not compiling when expression used as property name to acess function with an argument who's type has optional fields. TS2349
以下代码无法在 TypeScript 2.1.4 中编译并给出错误:
错误
Error:(6, 31) TS2349:Cannot invoke an expression whose type lacks a call signature. Type '((args: string[], action: A) => string[]) | ((args: string[], action: C) => string[])' has no compatible call signatures.
代码
/*
* 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"];
}
};
问题
我的问题是为什么首先显示错误,有人可以给我解释一下吗?
原因是A
和C
不兼容,因为prop1
在A
中是可选的,在C
中是必需的。因此,您不能在需要采用 A
的函数的地方使用采用 C
的函数:
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 时,它不能这样做,可能是因为它只是在编译时不计算键的表达式,所以它推断 map 值的联合类型,并且联合的两个成员彼此不兼容,因为A
和 C
不兼容。
您也可以通过明确声明类型 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"];
}
};
也有效。
以下代码无法在 TypeScript 2.1.4 中编译并给出错误:
错误
Error:(6, 31) TS2349:Cannot invoke an expression whose type lacks a call signature. Type '((args: string[], action: A) => string[]) | ((args: string[], action: C) => string[])' has no compatible call signatures.
代码
/*
* 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"];
}
};
问题
我的问题是为什么首先显示错误,有人可以给我解释一下吗?
原因是A
和C
不兼容,因为prop1
在A
中是可选的,在C
中是必需的。因此,您不能在需要采用 A
的函数的地方使用采用 C
的函数:
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 时,它不能这样做,可能是因为它只是在编译时不计算键的表达式,所以它推断 map 值的联合类型,并且联合的两个成员彼此不兼容,因为A
和 C
不兼容。
您也可以通过明确声明类型 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"];
}
};
也有效。