[英]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 }
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.所以我拥有的是一个
interface
,然后是一个实现getter
的base
类,然后是将它们的类型传递给基类的派生类。 Also there is an Id
type the holds unique identifier of handler as well as it's type.还有一个
Id
类型,它包含处理程序的唯一标识符及其类型。 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.每个处理程序在
id
变量中都有自己的 id,然后可以使用该处理程序类型的通用参数保存对Id
类型属性中其他处理程序的引用。
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
.我想要做的工作是正确键入
get
函数,以便它通过提供的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");
}
}
What I would like to achieve here is for the attributes aHandler
and bHandler
to be automatically inferred as Foo
and Goo
.我想在这里实现的是将属性
aHandler
和bHandler
自动推断为Foo
和Goo
。
You can user conditional types to extract the hander type from Id
.您可以使用条件类型从
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");
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.