有没有办法从其属性中推断出通用参数?

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> 依赖有问题的 Animalkind 属性 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
      }
    ];
  }
}

好的,希望对您有所帮助。祝你好运!

Link to code