我可以使用类型作为值(或从构造函数参数正确推断泛型类类型)吗? [英] Can I use type as value (or correctly infer generic class type from constructor parameter)?

查看:26
本文介绍了我可以使用类型作为值(或从构造函数参数正确推断泛型类类型)吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有通用类,它接受另一种类型和来自这种类型的键名.我还想用这个键名初始化属性.我已经在 TS 中定义了它,但我必须明确地将键的名称作为通用参数和相同的值传递给构造函数.看起来有点不必要——这些值总是相同的(类型和值).

是否可以从其构造函数参数推断泛型类的类型?或者使用通用的类型"作为值"(可能不是 - 我有错误:'ValueKey' 只指一个类型,但被用作一个值 here.ts(2693)).

带定义的代码:

interface InterfaceWithProperties {字符串属性:字符串;数字属性:数字;}class GenericCLass{keyNameFromAnotherType: ExactKey;//这个属性的值应该等于另一个类型的键;//有没有办法删除这个参数?它与 ExactKey 完全相同//或者根据 `property` 值正确检测 ExactKey 类型构造函数(keyNameFromAnotherType:ExactKey){//this.property = ExactKey;//ofc: 不工作的代码this.keyNameFromAnotherType = keyNameFromAnotherType;}}

当前使用情况:

const test1 = new GenericCLass('stringProperty');

我想要相同的结果但没有这个额外的参数.像这样:

const test2 = new GenericCLass();

或者像这样:

const test3 = new GenericCLass('stringProperty');

带有此代码的 TS 游乐场:链接

解决方案

问题是您想手动指定一个类型参数并让编译器推断另一个.这称为 部分类型参数推断,而 TypeScript 没有它(从 TS3.4).您可以手动指定所有类型参数(您不想这样做),也可以让编译器推断所有类型参数(您不能这样做,因为您无法从中推断出指定类型).

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

第一种是完全依赖类型推断并使用虚拟参数来推断您通常指定的类型.例如:

class GenericCLass{keyNameFromAnotherType: K;//添加虚拟参数构造函数(虚拟:T,keyNameFromAnotherType:K){this.keyNameFromAnotherType = keyNameFromAnotherType;}}const test = new GenericCLass(null! as InterfaceWithProperties, 'stringProperty');//推断为 GenericCLass

您可以看到作为第一个参数传入的值在运行时只是null,并且构造函数在运行时无论如何都不会查看它.但是类型系统已经被告知它是 InterfaceWithProperties 类型,这足以让类型以您想要的方式推断.

另一种解决方法是通过 currying 将通常使用部分推理的任何内容分解为两部分一>;第一个泛型函数让你指定一个参数,它返回一个泛型函数(在这种情况下是一个泛型构造函数)来推断另一个参数.例如

//不变class GenericCLass<T, K extends keyof T>{keyNameFromAnotherType: K;构造函数(keyNameFromAnotherType:K){this.keyNameFromAnotherType = keyNameFromAnotherType;}}//咖喱辅助函数const genericClassMaker =<T>(): (new <K extends keyof T>(keyNameFromAnotherType: K) =>GenericCLass <T,K>)=>通用类;//指定一个参数const InterfaceWithPropertiesGenericClass =genericClassMaker();//推断另一个参数const test = new InterfaceWithPropertiesGenericClass('stringProperty');//推断为 GenericCLass

这会留下您的类定义,但会创建一个新的辅助函数,该函数返回部分指定版本的 GenericClass 构造函数供您使用.你可以一次性完成,但很难看:

const test = new (genericClassMaker())('stringProperty');//推断为 GenericCLass

无论如何,希望其中之一对您有用.祝你好运!

I have generic class which accepts another type and the key name from this type. I want to also initialise property with this key name. I've defined this in TS but I have to explicitly pass the name of the key as generic param and the same same value to constructor. It looks a little unnecessary - these values are always the same (type and value).

Is it possible to infer type for generic class from its constructor parameter? Or use generic "type" as "value" (probably not - I've got error: 'ValueKey' only refers to a type, but is being used as a value here.ts(2693)).

Code with definition:

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;
    }
}

current usage:

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

I want the same result but without this extra parameter. Something like this:

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

or smth like this:

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

TS playground with this code: link

解决方案

The problem is that you want to manually specify one type parameter and have the compiler infer the other one. That's called partial type argument inference and TypeScript doesn't have it (as of TS3.4). You can either manually specify all type parameters (which you don't want to do), or have all type parameters inferred by the compiler (which you can't do because there's nothing from which you can infer the specified type).

There are two main workarounds for this situation:

The first is to rely entirely on type inference and use a dummy parameter to infer the type you would normally specify. For example:

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

You can see that the value passed in as the first parameter is just null at runtime, and the constructor doesn't look at it at runtime anyway. But the type system has been told that is is of type InterfaceWithProperties, which is enough for the type to be inferred the way you want.

The other workaround is to break up anything that would normally use partial inference into two pieces via currying; the first generic function will let you specify one parameter, and it returns a generic function (or a generic constructor in this case) that infers the other parameter. For example

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

That leaves your class definition alone but creates a new helper function which returns a partially specified version of the GenericClass constructor for you to use. You can do it in one shot but it's ugly:

const test = new (genericClassMaker<InterfaceWithProperties>())('stringProperty');
// inferred as GenericCLass<InterfaceWithProperties, "stringProperty">

Anyway, hope one of those works for you. Good luck!

这篇关于我可以使用类型作为值(或从构造函数参数正确推断泛型类类型)吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆