Typescript 类型脚本通用约束

Typescript 类型脚本通用约束,typescript,Typescript,我试图编写一个通用的getter函数,给定一个键,该键应该只返回类型字符串| boolean | number。因此,默认值应与返回值的类型相同 这是我试过的 getSomething = async<T extends string | boolean | number> (key: string, defaultValue: T): Promise<T> => { const foo = bar.getValue("foo"); // ret

我试图编写一个通用的getter函数,给定一个键,该键应该只返回类型字符串| boolean | number。因此,默认值应与返回值的类型相同

这是我试过的

getSomething = async<T extends string | boolean | number>
    (key: string, defaultValue: T): Promise<T> => {
      const foo = bar.getValue("foo"); // return type is undefined|string|boolean|number
      return foo === undefined ? defaultValue : foo;
 }

什么可能是“字符串|数字|布尔”的子类型让我感到困惑。它们不是已经是原始类型了吗?

我不得不将
foo转换为t

改变

return foo === undefined ? defaultValue : foo;

我不确定为什么我需要这个显式转换,因为
foo
肯定是
T
,因为它不是未定义的。
T
被限制为类型
string | boolean | number
,没有
bar
的定义,这不是一个。因为我只知道
bar.getValue()
返回一个类型为
string | number | boolean | undefined
的值,所以我将自己的
bar
如下:

const bar = { getValue: (x: string): string | number | boolean | undefined => 12345 };
然后我编写了这段代码,它没有编译器错误:

const str = Math.random().toString();
getSomething("baz", str).then(v => console.log(v.toUpperCase()));
如果在TypeScript IDE中使用IntelliSense检查
v
的类型,它将是
string
。这是因为
getSomething()
的调用签名表示它将返回与第二个参数相同类型的承诺。我们传入了
str
,一个
字符串
,所以我们得到了一个
承诺
,对吗

哦,没有。运行代码,您将得到一个运行时错误和一条消息,如
TypeError:v.toUpperCase不是一个函数
。因为在运行时,
v
将是
12345
,即从
bar.getValue()
返回的实际值。而
12345
是一个
number
,它没有
toUpperCase
方法。不知何故,我们陷入了一种情况,一个
数字
被误认为是
字符串
。错在哪里

这正是编译器警告您的地方:

return foo === undefined ? defaultValue : foo; // error!
// Type 'string | number | boolean | T' is not assignable to type 'T'.
TypeScript告诉您应该返回类型为
T
的值,但编译器只能验证您是否返回类型为
string | number | boolean | T
的值。在上面的例子中,
T
string
,因此您可以将错误解释为“您声称返回一个
字符串
,但我所知道的是您返回的是一个
字符串|数字|布尔值
,可能是
字符串
,但可能是
数字
布尔值
,在这种情况下,您的声明不正确,可能会发生坏事。”

希望您理解这是一个问题。
T
可以是
string
number
boolean
,它们都比联合类型
string | number | boolean
窄。您可以将
T
指定给
string | number | boolean
,但反之亦然


关于这个问题:“什么可能是
string | number | boolean
的子类型让我感到困惑。它们不是一个基本类型吗?“
string
string | number | boolean
的一个子类型。联合
a | B
是其每个成员
a
B
的超类型

此外,即使您只有
字符串
数字
布尔
,TypeScript中也有这些类型的子类型:有类型
“foo”
,有类型
123
,甚至布尔文字类型
true
false
(提到过,可能还有其他地方)。这些文字类型表示特定的值。因此类型
“a”
“b”
字符串的子类型,类型
1
2
number
的子类型,类型
true
false
布尔
的子类型

因此,实际上,当您使用第二个参数作为字符串、数字或布尔文本调用
getSomething()
时,这就是
T
的推断:

const p = getSomething("qux", "peanut butter and jelly");
// const p: Promise<"peanut butter and jelly">
编译时没有错误,而之前的代码有运行时错误但没有编译器错误,现在会给您一个不错的编译器错误:

getSomething2("baz", str).then(v => console.log(v.toUpperCase())); // error!
// ---------------------------------------------> ~~~~~~~~~~~
// Property 'toUpperCase' does not exist on type 'number'.
v
现在已知为
string | number | boolean
,您不能在该值上调用
toUpperCase
方法,因为它可能是
number
boolean

您可能需要将
getSomething()
作为泛型,但在这种情况下,
bar.getValue()
做什么真的很重要,可能需要修改
bar.getValue()
的签名,或者在
getSomething()中的某个地方进行明智的修改
您负责验证编译器无法验证的内容,并在运行时处理断言不真实的后果。您使用
return foo===undefined?defaultValue:foo as T;
的回答不太可能是正确的断言,特别是在文字类型方面。不过,我不会对这种方法作进一步的推测。我只想说,当您使用类型断言来消除编译器错误时,您需要仔细考虑您所做的声明


好的,希望能有帮助!祝你好运


此错误的示例:
getSomething('key','default')
。此处函数应返回
string
,但返回
string | boolean | number
。这是否回答了您的问题?请参阅上面的注释,解释此类强制转换可能有害的原因
const p = getSomething("qux", "peanut butter and jelly");
// const p: Promise<"peanut butter and jelly">
const getSomething2 = async (key: string, defaultValue: string | number | boolean):
    Promise<string | number | boolean> => {
    const foo = bar.getValue("foo"); 
    return foo === undefined ? defaultValue : foo;
}
getSomething2("baz", str).then(v => console.log(v.toUpperCase())); // error!
// ---------------------------------------------> ~~~~~~~~~~~
// Property 'toUpperCase' does not exist on type 'number'.