我可以使用Type As AS值(或从constructor参数中正确推断通用类类型)



我有通用类,该类接受另一种类型和此类型的键名。我还想使用此关键名称初始化属性。我已经在TS中定义了这一点,但是我必须明确将密钥的名称称为通用param,而相同的值则将相同的值传递给构造函数。它看起来有些不必要 - 这些值总是相同的(类型和值(。

是否可以从其构造函数参数中推断为通用类的类型?或使用通用" type"为" value"(可能不是 - 我有错误: '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'>();

或这样的smth:

const test3 = new GenericCLass<InterfaceWithProperties>('stringProperty');

ts Playground带有此代码:链接

问题是您要手动指定一个类型参数并让编译器推断另一个类型。这就是所谓的部分类型参数推理和打字稿没有它(从TS3.4开始(。您可以手动指定所有类型的参数(您不想这样做(,也可以具有编译器推断的所有类型参数(您不能这样做,因为您没有什么可以推断指定类型的类型(。

这种情况有两个主要的解决方法:

首先是完全依靠类型推理,并使用A 虚拟参数来推断您通常指定的类型。例如:

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,并且无论如何,构造函数在运行时都不会查看它。但是,已告知类型系统是InterfaceWithProperties类型,足以以您想要的方式推断该类型。

另一个解决方法是通过咖喱分解任何通常将部分推断为两部分的东西。第一个通用函数将使您指定一个参数,并且它返回一个占其他参数的通用函数(或在这种情况下是通用构造函数(。例如

// 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">

无论如何,希望其中一个为您服务。祝你好运!

不好的新事物是您不能轻易做到这一点。最好的方法是让编译器基于构造函数来推断ExactKey。仅当我们让打字稿推断所有类型参数时,才能完成。但是您需要指定一种类型的参数并推断另一个参数,这是不可能的,因为没有部分推断(至少在3.5起,都有一个计划添加它,但是将其推回去,然后从路线图(。

一个选项是使用静态功能而不是构造函数使用函数咖喱:

interface InterfaceWithProperties {
    stringProperty: string;
    numberProperty: number;
}
class GenericCLass<TypeWithKeys, ExactKey extends keyof TypeWithKeys> {
    keyNameFromAnotherType: ExactKey; 
    private constructor(keyNameFromAnotherType: ExactKey) {
        this.keyNameFromAnotherType = keyNameFromAnotherType;
    }
    static new<T>(){
        return function <ExactKey extends keyof T>(keyNameFromAnotherType: ExactKey) {
            return new GenericCLass<T, ExactKey>(keyNameFromAnotherType);
        }         
    } 
}

const test1 = GenericCLass.new<InterfaceWithProperties>()('stringProperty')

另一个选择是给编译器在构造函数args中的TypeWithKeys的推理网站:

interface InterfaceWithProperties {
    stringProperty: string;
    numberProperty: number;
}
class GenericCLass<TypeWithKeys, ExactKey extends keyof TypeWithKeys> {
    keyNameFromAnotherType: ExactKey; 
    constructor(t: TypeWithKeys, keyNameFromAnotherType: ExactKey) {
        this.keyNameFromAnotherType = keyNameFromAnotherType;
    }
}

const test1 = new GenericCLass(null as InterfaceWithProperties, 'stringProperty')

null as InterfaceWithProperties吓到您,您可以使用Type类:

class Type<T> { private t:T }
class GenericCLass<TypeWithKeys, ExactKey extends keyof TypeWithKeys> {
    keyNameFromAnotherType: ExactKey; 
    constructor(t: Type<TypeWithKeys>, keyNameFromAnotherType: ExactKey) {
        this.keyNameFromAnotherType = keyNameFromAnotherType;
    }
}

const test1 = new GenericCLass(new Type<InterfaceWithProperties>(), 'stringProperty')

这两个解决方案都不是很棒,也不是特别直观的,部分参数推断将是最好的解决方案。

相关内容

最新更新