有没有办法从其属性中推断出通用参数?
Is there a way to infer a generic parameter from its properties?
我正在尝试从另一个对象 属性 推断出一个对象 属性 的类型。
type Dog = { name: string };
type Cat = { age: number};
class AnimalType<T> {
}
class AnimalTypeRegistry {
static Dog: AnimalType<Dog>
static Cat: AnimalType<Cat>
}
interface IConfig<T> {
type: AnimalType<T>;
init: Partial<T>;
}
class Config {
get list(): IConfig<any>[] {
return [
{
type: AnimalTypeRegistry.Dog,
init: {
name: 'Bob' // Note - intellisense does not know this exists.
}
},
{
type: AnimalTypeRegistry.Cat,
init: {
age: 3 // Note - intellisense does not know this exists.
}
}
]
}
}
我希望 intellisense 能够推断出 init 是 Dog 类型并且名称 属性 存在。相反,它默认为任何(因为函数 return 类型)。
如果您使用 IConfig<any>
,您将失去所有类型提示,因为 any
可能是任何东西。
如果您想充分区分类型以获得对它们有意义的 IntelliSense,特别是固定 union of these types... and a discriminated union 会有所帮助:
// Use a discriminated union for types
type Dog = { name: string; kind: "Dog" };
type Cat = { age: number; kind: "Cat" };
type Animal = Dog | Cat;
这里,Animal
是判别联合:判别式属性被称为kind
,你可以使用字符串字面值"Dog"
和"Cat"
区分您的工会成员。
暂时避开你代码中的 "registry" 和“AnimalType<T>
”(如果你以后关心的话,你可以自己获得这样的功能),我会让 IConfig<K>
依赖有问题的 Animal
的 kind
属性 K
:
// have IConfig<K> depend on the discriminant K
interface IConfig<K extends Animal["kind"]> {
type: K;
init: Partial<Extract<Animal, { kind: K }>>;
}
请注意 init
属性 是 Partial<Extract<Animal, {kind: K}>>
。 Partial
对你有意义,但 Extract<Animal, {kind: K}>
服务于 extract Animal
联盟的特定成员,其 kind
属性 是 K
.
然后您希望 list()
输出一个包含 IConfig<"Dog"> | IConfig<"Cat">
个元素的数组。您可以通过编程方式从 Animal
生成该类型,如下所示:
// SomeConfig is the union of all possible IConfig<K> types
type SomeConfig = { [K in Animal["kind"]]: IConfig<K> }[Animal["kind"]];
// type SomeConfig = IConfig<"Dog"> | IConfig<"Cat">
(我们使用 mapped and a lookup 类型来做到这一点)。
注意你不想要IConfig<"Dog" | "Cat">
(联合在不同的地方),因为那些将允许合并元素,如{type: "Dog", init: { kind: "Cat" }}
,我假设你不想要。
最后你得到了你想要的 IntelliSense:
class Config {
get list(): SomeConfig[] {
return [
{
type: "Dog",
init: { name: "woofers" } // you get intellisense here
},
{
type: "Cat",
init: { age: 5 } // you get intellisense here
}
];
}
}
好的,希望对您有所帮助。祝你好运!
我正在尝试从另一个对象 属性 推断出一个对象 属性 的类型。
type Dog = { name: string };
type Cat = { age: number};
class AnimalType<T> {
}
class AnimalTypeRegistry {
static Dog: AnimalType<Dog>
static Cat: AnimalType<Cat>
}
interface IConfig<T> {
type: AnimalType<T>;
init: Partial<T>;
}
class Config {
get list(): IConfig<any>[] {
return [
{
type: AnimalTypeRegistry.Dog,
init: {
name: 'Bob' // Note - intellisense does not know this exists.
}
},
{
type: AnimalTypeRegistry.Cat,
init: {
age: 3 // Note - intellisense does not know this exists.
}
}
]
}
}
我希望 intellisense 能够推断出 init 是 Dog 类型并且名称 属性 存在。相反,它默认为任何(因为函数 return 类型)。
如果您使用 IConfig<any>
,您将失去所有类型提示,因为 any
可能是任何东西。
如果您想充分区分类型以获得对它们有意义的 IntelliSense,特别是固定 union of these types... and a discriminated union 会有所帮助:
// Use a discriminated union for types
type Dog = { name: string; kind: "Dog" };
type Cat = { age: number; kind: "Cat" };
type Animal = Dog | Cat;
这里,Animal
是判别联合:判别式属性被称为kind
,你可以使用字符串字面值"Dog"
和"Cat"
区分您的工会成员。
暂时避开你代码中的 "registry" 和“AnimalType<T>
”(如果你以后关心的话,你可以自己获得这样的功能),我会让 IConfig<K>
依赖有问题的 Animal
的 kind
属性 K
:
// have IConfig<K> depend on the discriminant K
interface IConfig<K extends Animal["kind"]> {
type: K;
init: Partial<Extract<Animal, { kind: K }>>;
}
请注意 init
属性 是 Partial<Extract<Animal, {kind: K}>>
。 Partial
对你有意义,但 Extract<Animal, {kind: K}>
服务于 extract Animal
联盟的特定成员,其 kind
属性 是 K
.
然后您希望 list()
输出一个包含 IConfig<"Dog"> | IConfig<"Cat">
个元素的数组。您可以通过编程方式从 Animal
生成该类型,如下所示:
// SomeConfig is the union of all possible IConfig<K> types
type SomeConfig = { [K in Animal["kind"]]: IConfig<K> }[Animal["kind"]];
// type SomeConfig = IConfig<"Dog"> | IConfig<"Cat">
(我们使用 mapped and a lookup 类型来做到这一点)。
注意你不想要IConfig<"Dog" | "Cat">
(联合在不同的地方),因为那些将允许合并元素,如{type: "Dog", init: { kind: "Cat" }}
,我假设你不想要。
最后你得到了你想要的 IntelliSense:
class Config {
get list(): SomeConfig[] {
return [
{
type: "Dog",
init: { name: "woofers" } // you get intellisense here
},
{
type: "Cat",
init: { age: 5 } // you get intellisense here
}
];
}
}
好的,希望对您有所帮助。祝你好运!