泛型 const 可以调用另一个泛型吗?
Can a generic const call another generic?
考虑以下使用泛型的 Typescript:
//Assume we've got data coming from somewhere we cannot control the type
const fetchFromSomewhere: (str: any) => any = (str: any) => str
const fetchOne: <T>(str: string) => T = (str: string) => {
const a = fetchFromSomewhere(str)
return a
}
//Does not work
const fetchTwo: <T>(str: string) => T = (str: string) => {
const a = fetchOne<T>(str) //Cannot find name 'T'.ts(2304)
return a
}
//Works
function fetchThree<T>(str: string): T {
const a = fetchOne<T>(str) //Can use <T> here
return a
}
fetchTwo
编译失败,说 Cannot find name 'T'.ts(2304)
。省略 <T>
会导致其 return 类型为 unknown
fetchThree
工作正常,是一个合适的解决方法。但是有没有一种方法可以使用 const 而不是函数来实现 fetchTwo
?
如果你想 annotate the call signature of an arrow function expression in TypeScript so that it is generic,你可以通过将类型参数声明紧接在参数列表之前来实现(并且这个列表必须放在括号中,即使只有其中一个)。例如,您可以转
const idArrow = x => x;
进入
const idArrow = <T>(x: T) => x;
类似于以下函数语句:
function idStatement<T>(x: T) { return x };
请注意,如果您正在使用 JSX 或您的 IDE 需要 JSX,则上述箭头函数可能会混淆它。毕竟,JSX 表达式看起来像 HTML 标签,<T>
看起来也像。为了防止这种混淆,您可以在类型参数后添加尾随逗号(类型参数列表允许尾随逗号):
const idArrow = <T,>(x: T) => x;
这完全等同于 idArrow
的先前定义,只是即使您的编译器设置为允许 JSX,此定义也能正常工作。
无论如何,这意味着您可以这样做:
const fetchOkay = <T,>(str: string) => {
const a = fetchOne<T>(str)
return a
}
如果未在调用签名中声明,则不能在箭头函数体内使用 T
参数:
const fetchBad = (str: string) => {
const a = fetchOne<T>(str) //Cannot find name 'T'.ts(2304)
return a
}
后一种情况正是您 fetchTwo
中发生的情况。
注意箭头函数表达式只是表达式;它们不必分配给变量。如果您确实将一个分配给变量,那么您可以独立选择 annotate the variable 或不。即可以注解:
const v: Type = init;
如果这样做,变量的类型注释将完全位于赋值运算符 (=
) 的左侧,并且初始化表达式不会直接参与其中。
在您的 fetchTwo
声明中,您使用通用函数类型注释 fetchTwo
变量:
const fetchTwo: <T>(str: string) => T = ...;
并且您的初始化值是之前有问题的箭头函数,其中 T
不在范围内。
(str: string) => {
const a = fetchOne<T>(str) //Cannot find name 'T'.ts(2304)
return a
}
希望您现在可以看到断开连接。我个人会放弃类型注释,让编译器推断 fetchTwo
变量的类型。但是如果你真的要注解,那就意味着你需要分别对变量和函数表达式的调用签名进行注解:
const fetchTwo: <T>(str: string) => T = <T,>(str: string) => {
const a = fetchOne<T>(str)
return a
}
而且那些真的是分开的;您可以重命名函数表达式中的函数参数和类型参数,而不会影响变量类型注释:
const fetchTwo: <T>(str: string) => T = <U,>(xxx: string) => {
const a = fetchOne<U>(xxx)
return a
}
考虑以下使用泛型的 Typescript:
//Assume we've got data coming from somewhere we cannot control the type
const fetchFromSomewhere: (str: any) => any = (str: any) => str
const fetchOne: <T>(str: string) => T = (str: string) => {
const a = fetchFromSomewhere(str)
return a
}
//Does not work
const fetchTwo: <T>(str: string) => T = (str: string) => {
const a = fetchOne<T>(str) //Cannot find name 'T'.ts(2304)
return a
}
//Works
function fetchThree<T>(str: string): T {
const a = fetchOne<T>(str) //Can use <T> here
return a
}
fetchTwo
编译失败,说 Cannot find name 'T'.ts(2304)
。省略 <T>
会导致其 return 类型为 unknown
fetchThree
工作正常,是一个合适的解决方法。但是有没有一种方法可以使用 const 而不是函数来实现 fetchTwo
?
如果你想 annotate the call signature of an arrow function expression in TypeScript so that it is generic,你可以通过将类型参数声明紧接在参数列表之前来实现(并且这个列表必须放在括号中,即使只有其中一个)。例如,您可以转
const idArrow = x => x;
进入
const idArrow = <T>(x: T) => x;
类似于以下函数语句:
function idStatement<T>(x: T) { return x };
请注意,如果您正在使用 JSX 或您的 IDE 需要 JSX,则上述箭头函数可能会混淆它。毕竟,JSX 表达式看起来像 HTML 标签,<T>
看起来也像。为了防止这种混淆,您可以在类型参数后添加尾随逗号(类型参数列表允许尾随逗号):
const idArrow = <T,>(x: T) => x;
这完全等同于 idArrow
的先前定义,只是即使您的编译器设置为允许 JSX,此定义也能正常工作。
无论如何,这意味着您可以这样做:
const fetchOkay = <T,>(str: string) => {
const a = fetchOne<T>(str)
return a
}
如果未在调用签名中声明,则不能在箭头函数体内使用 T
参数:
const fetchBad = (str: string) => {
const a = fetchOne<T>(str) //Cannot find name 'T'.ts(2304)
return a
}
后一种情况正是您 fetchTwo
中发生的情况。
注意箭头函数表达式只是表达式;它们不必分配给变量。如果您确实将一个分配给变量,那么您可以独立选择 annotate the variable 或不。即可以注解:
const v: Type = init;
如果这样做,变量的类型注释将完全位于赋值运算符 (=
) 的左侧,并且初始化表达式不会直接参与其中。
在您的 fetchTwo
声明中,您使用通用函数类型注释 fetchTwo
变量:
const fetchTwo: <T>(str: string) => T = ...;
并且您的初始化值是之前有问题的箭头函数,其中 T
不在范围内。
(str: string) => {
const a = fetchOne<T>(str) //Cannot find name 'T'.ts(2304)
return a
}
希望您现在可以看到断开连接。我个人会放弃类型注释,让编译器推断 fetchTwo
变量的类型。但是如果你真的要注解,那就意味着你需要分别对变量和函数表达式的调用签名进行注解:
const fetchTwo: <T>(str: string) => T = <T,>(str: string) => {
const a = fetchOne<T>(str)
return a
}
而且那些真的是分开的;您可以重命名函数表达式中的函数参数和类型参数,而不会影响变量类型注释:
const fetchTwo: <T>(str: string) => T = <U,>(xxx: string) => {
const a = fetchOne<U>(xxx)
return a
}