我的问题有点复杂,所以这里可能是所有必需的部分:
// 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
,然后是一个实现base
的getter
类,然后派生类将它们的类型传递给基类。还有一个Id
类型,它包含处理程序的唯一标识符以及它的类型。每个处理程序在id
变量中都有自己的id,然后可以使用该处理程序类型的泛型参数保存对Id
类型的属性中的其他处理程序的引用。
我想做的工作是正确键入get
函数,以便通过提供key
来推断它所获得的处理程序类型。由于每个键都按其指向的处理程序类型进行模板化,因此该类型可能用于推断返回类型,但是使用这样的设置它不起作用。
您可以在此处查看所需用法的示例:
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
。
您可以使用条件类型从Id
中提取hander类型。我也改变了你获取密钥的方式,它不能用于严格的空检查:
// 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");
}
}