根据泛型类型中其值的类型来约束 Key

Posted

技术标签:

【中文标题】根据泛型类型中其值的类型来约束 Key【英文标题】:Constrain a Key based on the type of its value in a generic type 【发布时间】:2021-08-10 12:53:57 【问题描述】:

我正在尝试编写一个泛型函数,其中泛型类型用于键入其参数。

这是这个参数的一部分:

type Configuration<T> = 
    masterdata: T[],
    target: ????

我在输入 target 属性时遇到问题。我希望它是特定类(MtmFormComponent,当前类)中的任何属性的名称,但限制属性的类型是T[]

我的目标是写:this[configuration.target] = configuration.masterdata;

我已经完成了一半。以下是我现在输入target 的方式:

type MtmFormComponentPropertyOfType<T, K extends keyof MtmFormComponent> = MtmFormComponent[K] extends T[] ? K : never;

type DropdownAutocompleteConfiguration<T, K extends keyof MtmFormComponent> = 
    masterdata: T[],

    targetFilteredList: MtmFormComponentPropertyOfType<T, K>,
;

当声明 DropdownAutocompleteConfiguration 类型的对象时,一切都很好,我的 IDE 的自动完成正确地限制我只使用导致与 masterdata 的值相同类型的键。所以我的类型似乎被正确定义了。

我的问题是在我的通用函数中使用该对象时:

private setupDropdownAutocompleteBehaviour<T, K extends keyof MtmFormComponent>(configuration: DropdownAutocompleteConfiguration<T, K>): void 
        this[configuration.targetFilteredList] = configuration.masterdata;

        // ...
    

这里,this[configuration.targetFilteredList] 表达式给出以下错误:

类型 'T[]' 不能分配给类型 'this[Currency[] extends T[] ? "filteredCurrencyList" : never] & this[PriceUnit[] extends T[] ? "filteredPriceUnits" : never] & this[Subscription[] extends T[] ? “订阅”:从不]'。 类型 'T[]' 不能分配给类型 'this[Currency[] extends T[] ? “filteredCurrencyList”:从不]'。 类型“T[]”不可分配给类型“Currency[]”。 类型“T”不能分配给类型“货币”。

据我了解,在函数内部,TypeScript 完全解析了this[configuration.targetFilteredList] 的类型,而不是依赖于它是T[] 的事实。从我的 IDE 的自动完成功能来看,我确信我不能为 target 设置值,这会导致类型与 masterdata 不兼容。

我不知道从那里做什么:/

感谢您的帮助:)

【问题讨论】:

【参考方案1】:

在这种特殊情况下,您是否必须使您的函数在目标属性键上通用?

举个具体的例子,假设MtmFormComponent是这样定义的

interface MtmFormComponent 
    foo: string[];
    bar: string[];
    baz: number[];
    qux: string;

现在,我们需要一个类型,它可以为我们提供T 类型的所有属性名称,其中属性名称映射到U[] 的值。我们可以这样定义这种类型:

type ArrayValuedProps<T, U> =  [K in keyof T]: (T[K] extends U[] ? K : never) [keyof T]

这样,我们得到

ArrayValuedProps<MtmFormComponent, string> =  foo: "foo"; bar: "bar"; baz: never; qux: never [keyof MtmFormComponent]
                                           =  foo: "foo"; bar: "bar"; baz: never; qux: never ["foo" | "bar" | "baz" | "qux"]
                                           = "foo" | "bar" | never
                                           = "foo" | "bar"

同样,ArrayValuedProps&lt;MtmFormComponent, number&gt; 等价于类型"baz"

使用它,我们可以像这样定义您的Configuration&lt;T&gt; 类型:

type Configuration<T> = 
    masterdata: T[],
    target: ArrayValuedProps<MtmFormComponent, T>

我们可以完全避免将目标键作为类型参数。

【讨论】:

以上是关于根据泛型类型中其值的类型来约束 Key的主要内容,如果未能解决你的问题,请参考以下文章

TS泛型类、泛型接口、泛型函数

Kotlin泛型总结 ★ ( 泛型类 | 泛型参数 | 泛型函数 | 多泛型参数 | 泛型类型约束 | 可变参数结合泛型 | out 协变 | in 逆变 | reified 检查泛型参数类型 )

在没有泛型类约束的情况下,将泛型类型与其默认值进行比较,会产生编译时错误

C#泛型 类型约束

仅适用于数值类型的泛型类型约束

泛型方法