首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >派生包装类静态方法的自动推理

派生包装类静态方法的自动推理
EN

Stack Overflow用户
提问于 2022-10-30 14:24:29
回答 1查看 46关注 0票数 1

我希望在基类上定义的静态方法(充当构造器)能够自动推断派生类和包装类型。我能推断出其中一种,但不能两者兼而有之。一个简化的示例和一个需要处理的游乐场

代码语言:javascript
复制
export class Collection<T> {
    constructor(private _xs: T[]) {}

    static fromArray1<C extends typeof Collection, T>(this: C, xs: T[]): InstanceType<C> {
        return new this(xs) as InstanceType<C>;
    }

    static fromArray2<C extends Collection<T>, T>(this: new (xs: T[]) => C, xs: T[]): C {
        return new this(xs);
    }
}

export class ExtendedCollection<T> extends Collection<T> {}

// Goal: automatically infer as ExtendedCollection<number>
const ec1 = ExtendedCollection.fromArray1([1, 2, 3]);
const ec2 = ExtendedCollection.fromArray2([1, 2, 3]);
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2022-10-31 02:53:08

实现目标的主要障碍是在TypeScript中缺乏对更高类型类型的直接支持,这是微软/打字稿#1213中所要求的类型。现在您可以构建像type Gen<T extends Foo> = ...这样的泛型类型,其中类型参数T表示某些特定类型,但是您不能对此进行抽象,以生成一个更高的泛型类型,比如type Higher<F<~> extends Gen<~>> = ...,其中类型参数F表示某些泛型类型,它本身带有一个类型参数,比如Gen

如果你可以的话,也许你就能写

代码语言:javascript
复制
// NOT VALID TS, DON'T TRY THIS:
static fromArray<C<~> extends Collection<~>, T>(
  this: new (...args: any) => C<any>, 
  xs: T[]
): C<T>;

并将泛型类型参数C“应用”到类型T以生成C<T>。但这不是直接支持的,所以我们必须绕开它。

这个问题,microsoft/TypeScript#1213,提到了几种解决方法。它们目前都需要一些需要维护的样板代码。例如,如果您愿意为每个定义的子类将属性合并到接口中,您可以定义一个“应用”合并类型函数:

代码语言:javascript
复制
export class Collection<T> {
    constructor(private _xs: T[]) { }
    static fromArray<C extends typeof Collection, T>(this: C, xs: T[]): Apply<C, T> {
        return new this(xs) as any;
    }
    x = 0

}

interface HKT<T> { Collection: Collection<T> }

type Apply<C extends new (...args: any) => any, T> =
    { [K in keyof HKT<any>]:
        HKT<any>[K] extends InstanceType<C> ?
        InstanceType<C> extends HKT<any>[K] ?
        HKT<T>[K] : never : never }[keyof HKT<any>]

从上面可以看出,Apply<typeof Collection, number>将生成Collection<number>。然后,对于子类,您可以在另一个属性中合并:

代码语言:javascript
复制
export class ExtendedCollection<T> extends Collection<T> {
    y = 1
}
// manually merge in this
interface HKT<T> { ExtendedCollection: ExtendedCollection<T> }

现在,Apply<typeof ExtendedCollection, string>将生产ExtendedCollection<number>。因此,您的示例将按需要工作:

代码语言:javascript
复制
const ec1 = ExtendedCollection.fromArray([1, 2, 3]);
// const ec1: ExtendedCollection<number>
ec1.y

在不尝试模拟更高类型的情况下,我唯一能想到的其他解决方法就是放弃抽象"apply C to T“,而只是手动缩小每个子类,从而使其方法对自己有硬编码的引用:

代码语言:javascript
复制
export class Collection<T> {
    constructor(private _xs: T[]) { }
    static fromArray<T>(xs: T[]): Collection<T> {
        return new this(xs) as any;
    }
    x = 0
}

这就是基类,然后子类只会适当地标记它们的方法类型,而不实际修改它们:

代码语言:javascript
复制
export class ExtendedCollection<T> extends Collection<T> {
    static fromArray: <T>(xs: T[]) => ExtendedCollection<T>;
    y = 1;
}

同样,该示例的工作原理是:

代码语言:javascript
复制
const ec1 = ExtendedCollection.fromArray([1, 2, 3]);
// const ec1: ExtendedCollection<number>
ec1.y

显然,这两种方法都不是完美的。也许还有其他更适合你需求的解决办法,但是除非微软/类型编号1213被实现,否则我认为没有任何完全没有痛苦的方法可以做到。

操场链接到代码

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/74253853

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档