从对象的键推断泛型类型 [英] Infer generic type from key of object
问题描述
我的问题有点复杂,所以这里可能是所有必需的部分:
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
,然后是一个实现 getter
的 base
类,然后是将它们的类型传递给的派生类基类.还有一个 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");
}
}
我想在这里实现的是将属性 aHandler
和 bHandler
自动推断为 Foo
和 Goo代码>.
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屋!