我有一个通用的类,它接受其他类型和该类型的键名。我还想用此键名初始化属性。我已经在TS中定义了它,但是我必须将键的名称显式传递为泛型参数,并将相同的值传递给构造函数。看起来有点不必要-这些值始终相同(类型和值)。
是否可以从其构造函数参数推断通用类的类型?或者使用通用的“类型”作为“值”(可能不是-我遇到了错误:)'ValueKey' only refers to a type, but is being used as a value here.ts(2693)
。
定义代码:
interface InterfaceWithProperties {
stringProperty: string;
numberProperty: number;
}
class GenericCLass<TypeWithKeys, ExactKey extends keyof TypeWithKeys> {
keyNameFromAnotherType: ExactKey; // this property should have value equal to key from another type;
// is it a way to remove this parameter? it's exactly duplicated with ExactKey
// or to detect correctly ExactKey type based on `property` value
constructor(keyNameFromAnotherType: ExactKey) {
// this.property = ExactKey; // ofc: not working code
this.keyNameFromAnotherType = keyNameFromAnotherType;
}
}
当前用法:
const test1 = new GenericCLass<InterfaceWithProperties, 'stringProperty'>('stringProperty');
我想要相同的结果,但没有这个额外的参数。像这样:
const test2 = new GenericCLass<InterfaceWithProperties, 'stringProperty'>();
或像这样:
const test3 = new GenericCLass<InterfaceWithProperties>('stringProperty');
带有以下代码的TS游乐场:链接
问题是您要手动指定一个类型参数,并让编译器推断另一个参数。这就是所谓的部分类型参数推论,而TypeScript则没有(从TS3.4开始)。您既可以手动指定所有类型参数(不想执行),也可以让所有类型参数由编译器推断(您不能这样做,因为您无法从中推断出指定的类型)。
对于这种情况,有两种主要的解决方法:
第一种是完全依靠类型推断,并使用虚拟参数来推断您通常指定的类型。例如:
class GenericCLass<T, K extends keyof T> {
keyNameFromAnotherType: K;
// add dummy parameter
constructor(dummy: T, keyNameFromAnotherType: K) {
this.keyNameFromAnotherType = keyNameFromAnotherType;
}
}
const test = new GenericCLass(null! as InterfaceWithProperties, 'stringProperty');
// inferred as GenericCLass<InterfaceWithProperties, "stringProperty">
您可以看到作为第一个参数传入的值只是null
在运行时,而构造函数无论如何都不会在运行时查看它。但是类型系统已经被告知是type InterfaceWithProperties
,这足以以您想要的方式推断类型。
另一个解决方法是通过currying将通常使用部分推断的任何东西分解为两部分;第一个泛型函数将允许您指定一个参数,并返回一个推断另一个参数的泛型函数(在本例中为泛型构造函数)。例如
// unchanged
class GenericCLass<T, K extends keyof T> {
keyNameFromAnotherType: K;
constructor(keyNameFromAnotherType: K) {
this.keyNameFromAnotherType = keyNameFromAnotherType;
}
}
// curried helper function
const genericClassMaker =
<T>(): (new <K extends keyof T>(
keyNameFromAnotherType: K
) => GenericCLass<T, K>) =>
GenericCLass;
// specify the one param
const InterfaceWithPropertiesGenericClass =
genericClassMaker<InterfaceWithProperties>();
// infer the other param
const test = new InterfaceWithPropertiesGenericClass('stringProperty');
// inferred as GenericCLass<InterfaceWithProperties, "stringProperty">
这样GenericClass
就不用再定义类了,而是创建一个新的帮助器函数,该函数返回部分指定版本的构造函数供您使用。您可以一次完成,但是很丑陋:
const test = new (genericClassMaker<InterfaceWithProperties>())('stringProperty');
// inferred as GenericCLass<InterfaceWithProperties, "stringProperty">
无论如何,希望其中一项对您有用。祝好运!
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句