从对象的键推断泛型类型 [英] Infer generic type from key of object

查看:25
本文介绍了从对象的键推断泛型类型的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的问题有点复杂,所以这里可能是所有必需的部分:

My question is a bit more complex so here are possibly all the required parts:

// Common interface with an id and a string lieral type
interface IHandler {
    id: Id<IHandler>;
    type: string;
}

// Base class for all the handler, with generic argument providing string literal type and access to inheriting type
class Base<Type extends string, T extends Base<Type, T>> implements IHandler {
    id: Id<T> = Guid.raw() as any

    // In consturctor string literal type is instantiated
    protected constructor(public type: Type) {
        this.type = type;
    }

    // Static function that retrieves handler by it's key from a store
    static GetHandler: <T extends IHandler>(get: Id<T>) => T;

    // Getter that accepts key of an id attribute and returns instance from store
    get = <Handler extends Base<Handler["type"], Handler>>(key: HandlerIds<T, Handler>) => Base.GetHandler<Handler>((this as any)[key]);
}

// Keys of attributes that are Id's
type HandlerIds<T extends Base<T["type"], T>, U extends Base<U["type"], U>> = keyof SubType<Model<T>, Id<U>>;

// Creates a subtype based on condition
type SubType<Base, Condition> = Pick<Base, { [Key in keyof Base]: Base[Key] extends Condition ? Key : never }[keyof Base]>;

// Excludes attributes of Base
type Model<T extends Base<T["type"], T>> = Partial<Pick<T, Exclude<keyof T, keyof Base<T["type"], T>>>>;

// Type that holds guid reference and also a type that guid is supposed to be pointing to
type Id<T extends IHandler> = string & { __type: T }

所以我有一个 interface,然后是一个实现 getterbase 类,然后是将它们的类型传递给的派生类基类.还有一个 Id 类型,它包含处理程序的唯一标识符及其类型.每个处理程序在 id 变量中都有自己的 id,然后可以使用该处理程序类型的通用参数保存对 Id 类型属性中的其他处理程序的引用.

So what I have is an interface, then a base class that implements a getter, and then derived classes that pass their type to the base class. Also there is an Id type the holds unique identifier of handler as well as it's type. Each handler has it's own id in id variable and then can save references to other handlers in attribute of type Id with generic argument of that handler's type.

我想要做的是正确键入 get 函数,以便它通过提供的 key 推断它正在获取的处理程序的类型.由于每个键都是由它指向的处理程序类型模板化的,因此这种类型可能应该用于推断返回类型,但是像这样的设置它不起作用.

What I would like to make work is to properly type the get function so it infers the type of handler it is getting by provided key. Since each key is templated by type of handler it is pointing to, this type should be probably used to infer the return type, but with setup like this it does not work.

您可以在此处查看所需用法的示例:

You can see an example of desired usage here:

class Foo extends Base<"Foo", Foo> {
    a: Id<Goo>;
    b: Id<Foo>;
    constructor() {
        super("Foo");
        // Get a reference to instance of "a" of type Goo
        var aHandler = this.get("a");
        // Get a reference to instance of "b" of type Foo
        var bHandler = this.get("b");
    }
}
class Goo extends Base<"Goo", Goo> {
    constructor() {
        super("Goo");
    }
}

我想在这里实现的是将属性 aHandlerbHandler 自动推断为 FooGoo.

What I would like to achieve here is for the attributes aHandler and bHandler to be automatically inferred as Foo and Goo.

推荐答案

您可以使用条件类型从 Id 中提取处理程序类型.我还改变了您获取密钥的方式,它不适用于严格的空检查:

You can user conditional types to extract the hander type from Id. I also changed a bit the way you were getting the keys, it did not work with strict null checks :

// Common interface with an id and a string lieral type
interface IHandler {
    id: Id<IHandler>;
    type: string;
}

// Base class for all the handler, with generic argument providing string literal type and access to inheriting type
class Base<Type extends string, T extends Base<Type, T>> implements IHandler {
    id: Id<T> = Guid.raw() as any

    // In consturctor string literal type is instantiated
    protected constructor(public type: Type) {
        this.type = type;
    }

    // Static function that retrieves handler by it's key from a store
    static GetHandler: <T extends IHandler>(get: Id<T>) => T;

    // Getter that accepts key of an id attribute and returns instance from store
    get<K extends HandlerIds<T, Array<Id<any>>>>(key: K, index: Extract<keyof T[K], number>) : HandlerFromIdArray<T[K]> 
    get<K extends HandlerIds<T, Id<any>>>(key: K) : HandlerFromId<T[K]>
    get<K extends HandlerIds<T, Id<any>>>(key: K, index?: number) : IHandler { 
        return Base.GetHandler<IHandler>((this as any)[key]) as any; // this assertion is needed 
    }
}

// Keys of attributes that are Id's
type HandlerIds<T extends Base<T["type"], T>, C> = Exclude<{ [P in keyof T]-?: T[P] extends C ? P : never}[keyof T], keyof Base<string, any>>;

// Type that holds guid reference and also a type that guid is supposed to be pointing to
type Id<T extends IHandler> = string & { __type: T }
type HandlerFromId<T> = T extends Id<infer U> ? U: never;
type HandlerFromIdArray<T> = T extends Array<Id<infer U>> ? U: never;

class Foo extends Base<"Foo", Foo> {
    a!: Id<Goo>;
    b!: Id<Foo>;
    c!: Id<Foo>[];
    constructor() {
        super("Foo");
        // Get a reference to instance of "a" of type Goo
        var aHandler = this.get("a");
        // Get a reference to instance of "b" of type Foo
        var bHandler = this.get("b");

        var cHandler = this.get("c", 1); // Foo
    }
}
class Goo extends Base<"Goo", Goo> {
    constructor() {
        super("Goo");
    }
}

这篇关于从对象的键推断泛型类型的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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