我对TypeScript的类型推理有些理解。我认为这应该是有效的,不需要我指定T
:的类型
export const deflt = <T>(val: (T | undefined | null), defaultVal: T): T => {
if (val === undefined || val === null) {
return defaultVal;
}
return val;
}
function maybeLabelName(name: string | undefined) {
return deflt(name, 'untitled');
}
对deflt的调用失败,并出现以下错误:
Argument of type 'string | undefined' is not assignable to parameter of type '"untitled" | null | undefined'.
Type 'string' is not assignable to type '"untitled" | null | undefined'. [2345]
我可以通过将deflt
调用更改为以下之一来修复它:
// Option 1:
return deflt<string>(name, 'untitled');
// Option 2:
const s: string = 'untitled';
return deflt(name, s);
TypeScript不应该自动推断string
类型吗?
在这种情况下,typescript推断T
的方式有点奇怪。因为您指定了常量'untitled'
,所以编译器会将T
推断为字符串文字类型'untitled'
,因为这是从中推断T
的最简单的站点。然后,它将对照字符串文字类型检查另一个参数,并发现string
不可分配给'untitled'
。解决方法是在通过添加与{}
:的额外交集来推断T
时降低第二个参数的优先级
export const deflt = <T>(val: (T | undefined | null), defaultVal: T & {} ): T => {
if (val === undefined || val === null) {
return defaultVal;
}
return val;
}
function maybeLabelName(name: string | undefined) {
return deflt(name, 'untitled'); // T is string now
}
另一个解决方案(如果不能更改原始函数(是显式断言untitled
为string
,这基本上是您已经尝试过的。
function maybeLabelName(name: string | undefined) {
return deflt(name, 'untitled' as string); // T is string now
}
当您使用泛型时,可以有两种不同的方法来"生成"参数。
以您的案例为例:
const deflt = <T>(val: (T | undefined | null), defaultVal: T): T
如果使用:deflt<string>(a, b)
,则强制a
为string | undefined | null
,强制b
为string
。
如果使用:deflt(a: string, b: "untitled")
,则转发器将"untitled"
理解为常量,而不是字符串。因此,b
将是"untitled"
类型,从而使得T
是"untitled"
类型。由于您使用的是string
类型的a
,因此编译器无法将string
强制转换为"untitled"
。
解决方案已经由@Titian Cernicova Dragomir回答提供
您已将deflt声明为泛型函数,但已将其用作正则函数
const deflt = <T>(val: (T | undefined | null), defaultVal: T): T => {
if (val === undefined || val === null) {
return defaultVal;
}
return val;
}
function maybeLabelName(name?: string) {
return deflt<typeof name>(name, 'untitled');
}