我有一个带有2个参数的方法,我希望它从第一个参数推断类型。
例如,在下面的代码中,我希望从T
推断出函数create_C<T>
的类型firstArgument
,以便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
答案 0 :(得分:3)
在您的功能签名中
declare function create_C<T>(a1: A<T>, a2: (obj: any) => T): C<T>;
T
有两个推断站点(“推断站点”是指“某个地方编译器可用来尝试为类型参数推断类型”)。一个站点来自第一个参数type
的{{1}}属性,另一个站点来自第二个参数a1
的返回类型。编译器会像这样调用
a2
并尝试从两个站点推断create_C(new B_Component(), () => ({ prop2: 2 });
。在这种情况下,存在一个匹配项:T
和(new B_Component()).type
都可以分配给{prop2: 2}
。这样就没有错误,并且您得到{prop2: number}
的提示。在另一种情况下,这可能正是您希望编译器提供的行为。
相反,您希望看到编译器仅使用C<{prop2: number>
来推断a1
,并仅验证T
与之匹配。也就是说,您希望a2
中的T
是non-inferential type parameter (see microsoft/TypeScript#14829)。不幸的是,对此没有“官方”支持。但是幸运的是,有一些变通方法通常可以用来获得此行为。
这是一种这样的技术:如果将推理站点中的类型参数从(obj: any) => T
更改为T
,则将其更改为lowers the site's priority。因此,编译器将倾向于首先从其他推断站点推断T & {}
,并且只有在无法从其他地方推断时才返回到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
的输出。
好的,希望能有所帮助;祝你好运!
答案 1 :(得分:0)
这是由于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 }));