打字稿泛型:从函数参数的类型推断类型?
Posted
技术标签:
【中文标题】打字稿泛型:从函数参数的类型推断类型?【英文标题】:Typescript generics: infer type from the type of function arguments? 【发布时间】:2020-03-22 02:54:53 【问题描述】:我有一个有 2 个参数的方法,我希望它从第一个参数推断出一个类型。
例如,在下面的代码中,我希望从firstArgument
中推断出函数create_C<T>
的类型T
,以便create_C
函数的返回类型为C<type inferred from firstArgument>
interface C<T>
firstArgument: A<T>;
secondArgument: (obj: any) => T
export interface A<T>
type: T;
function create_C<T>(
firstArgument: A<T>,
secondArgument: (obj: any) => T
): C<T>
return
firstArgument,
secondArgument
但是,在下面的实现中,const c
的类型被推断为C< prop2: number >
。但我希望它被推断为C<B>
,并且我希望编译器会抛出一个错误,指出secondArgument
的返回类型不是B
类型
interface B
prop1: string;
prop2: number
export class B_Component implements A<B>
type: B =
prop1: "",
prop2: 1
;
const c = create_C(
new B_Component(),
() => ( prop2: 2 )
)
如何确保编译器抛出错误,指出 secondArgument
的返回类型不是 B
类型?
这里是 Stackblitz 编辑器链接:https://stackblitz.com/edit/qqddsn
【问题讨论】:
不完全关注但你不应该像这样打电话给create_C
吗?:create_C<B_Component>()
。您使用泛型键入函数,一切看起来都不错,只需在调用时将正确的类型传递给 T...
那么您是否希望您的create_C
调用出错?作为第二个参数传递的回调不返回B
,它返回prop2: number
,所以c
是C<prop2: number>
。为什么不在回调中返回一个实际的B
?
@MrRobboto 我不想用泛型调用函数。我希望根据传递的第一个参数的类型推断泛型
很确定您不应该将 create_C 键入为采用泛型 then
并且应该读入 @jcalz 评论 - () => ( prop2: 2 )
不适合您使用 secondArgument: (obj: any) => T
键入的内容
【参考方案1】:
在你的函数签名中
declare function create_C<T>(a1: A<T>, a2: (obj: any) => T): C<T>;
T
有两个推理站点(“推理站点”的意思是“编译器可以用来尝试为类型参数推断类型的某个地方”)。一个站点来自第一个参数a1
的type
属性,另一个站点是第二个参数a2
的返回类型。编译器会查看类似的调用
create_C(new B_Component(), () => ( prop2: 2 );
并尝试从两个站点推断T
。在这种情况下,有一个匹配项:(new B_Component()).type
和 prop2: 2
都可以分配给 prop2: number
。所以没有错误,你会得到C<prop2: number>
出来。在另一种情况下,这可能正是您希望编译器提供的行为。
相反,您希望看到编译器只使用a1
来推断T
,并验证a2
是否匹配它。也就是说,您希望(obj: any) => T
中的T
成为non-inferential type parameter (see microsoft/TypeScript#14829)。不幸的是,对此没有“官方”支持。但幸运的是,通常可以使用一些变通方法来获得这种行为。
这是一种这样的技术:如果您将推理站点中的类型参数从T
更改为T &
,则它是lowers the site's priority。因此,编译器将倾向于首先从其他推理站点推断T
,并且只有在无法从其他位置推断时才返回T &
。并且T &
类型与T
非常相似(如果T
是一个对象类型,那么它基本上是相同的)所以它不会对语义有太大的改变。让我们试试吧:
declare function create_C_better<T>(a: A<T>, b: (obj: any) => T & ): C<T>;
这里是:
const c2 = create_C_better(
new B_Component(),
() => ( prop2: 2 ) // error!
// ~~~~~~~~~~~~~~ <-- prop1 is missing
)
const c3 = create_C_better(
new B_Component(),
() => ( prop1: "all right", prop2: 2 )
); // C<B>
在那里,当prop1
丢失时,你会得到你想要的错误,当你修复它时,你会根据需要得到C<B>
类型的输出。
好的,希望对您有所帮助;祝你好运!
Link to code
【讨论】:
很好的解释。应该在代码中引用,因为没有人会知道&
的用途
感谢您抽出宝贵时间提供帮助!真的很棒的解释。【参考方案2】:
这是由于secondArgument: (obj: any) => T
。
如果您将上述定义应用于() => ( prop2: 2 )
T
的类型是 prop2: number
。您可以将其更改为其他内容以获得所需的结果。
例如。
interface C<T>
firstArgument: A<T>;
secondArgument: (obj: any) => any;
export interface A<T>
type: T;
declare function create_C<T>(
firstArgument: A<T>,
secondArgument: (obj: any) => any
): C<T>;
interface B
prop1: string;
prop2: number;
export class B_Component implements A<B>
type: B;
configuration: B =
prop1: "",
prop2: 1
;
const b = new B_Component();
export const c = create_C(b, () => ( prop2: 2 ));
【讨论】:
嘿,你发布的那个东西还是不行。 T 的类型是 STILL prop2: number 这很奇怪。请检查这个 ts 游乐场链接。 typescriptlang.org/play/… 鼠标悬停在c
上是预期的类型。
但它并不强制第二个参数的返回类型为 B
我明白你的意思。就像第一个答案 ` (obj: any) => T & ` 应该修复它。以上是关于打字稿泛型:从函数参数的类型推断类型?的主要内容,如果未能解决你的问题,请参考以下文章