是否可以根据传递的泛型类型定义属性名称?
type Foo<T> = {
// magic goes here
someProp: boolean
};
enum MyEnum {
red = 'red',
blue= 'blue',
}
const d: Foo<MyEnum.blue> = null;
d.someProp
d.blue // Property 'blue' does not exist on type 'Foo<MyEnum.blue>'.ts(2339)
一直在查看文档,但我无法弄清楚。
答案 0 :(得分:1)
您可以使用 mapped type 在给定其键和关联值的类型的情况下动态生成对象类型。例如,类型 {[P in "x" | "y"]: [P]}
产生类型 {x: ["x"], y: ["y"]}
。对于值类型不依赖于键的情况,您可以使用内置的 Record<K, V>
实用程序类型,详细说明 here。所以 Record<"x" | "y", number>
等价于 {x: number, y: number}
。
在您的情况下,您希望 Foo<T>
拥有密钥 T
,这意味着我们需要将 constrain T
设为 keylike 类型(即 {{1} },或者您可以使用 built in PropertyKey
alias)。在这种情况下,string | number | symbol
是比 K
更传统的参数名称。让我重新开始:
您希望 T
将 Foo<K extends PropertyKey>
作为键。我不确定你想要那个键的值类型;现在我只使用 any
, the ultimate ?♂️ type。并且您还希望 K
在 Foo<K>
键处有一个 boolean
属性。所以它应该是 both "someProp"
,and 一个 Record<K, any>
。这意味着您希望它成为其中的 intersection:
{someProp: boolean}
您可以验证这是否有效:
type Foo<K extends PropertyKey> = {
someProp: boolean
} & Record<K, any>
请注意,您不能将 const d: Foo<MyEnum.blue> = {
blue: 123,
someProp: true
}
变成 Foo
。在 interface
和 class
类型中,编译器需要在使用之前静态地知道键:
interface
对于 interface Oops<K extends PropertyKey>
extends Foo<K> { } // error!
// -----> ~~~~~~
// An interface can only extend an object type or
// intersection of object types with statically known members.
的特定规范,您可以创建一个接口:
K