TypeScript 请求:允许抽象类实现带有类型参数的MappedTypes,

tp5buhyn  于 4个月前  发布在  TypeScript
关注(0)|答案(9)|浏览(57)

好的,标题有点拗口,但这是我们试图实现的目标的一个例子:

interface DeferredProperty<T> {
    get(): T;
}

type Deferred<T> = {
    [P in keyof T]: DeferredProperty<T[P]>
};

在这里,我们使用Map类型来表达这样一个想法:一个接口类型可能会被 Package 成一个新类型,该新类型暴露与它相同的属性,只是将函数作为数据属性而不是。例如:

interface Person {
    age: number,
    name: string
}
var deferredPerson: Deferred<Person>;
var age = deferredPerson.age.get();

interface Vehicle {
    numWheels: number,
    cost: number
}
var deferredVehicle: Deferred<Vehicle>;
var cost = deferredVehicle.cost.get();

我们希望在我们系统中使很多类型可延迟,所以我们创建了一个简单的抽象基类,它可以帮助我们完成一些工作。换句话说,我们希望能够写:

abstract class BaseDeferred<T> implements Deferred<T> { //<-- note: currently not legal
    // some common stuff, including concreate methods

    // Maybe some abstract methods.
    // protected abstract whatever(): void;

    // etc.
}

然后我们可以这样做:

class DeferredPerson extends BaseDeferred<Person> {
}
class DeferredVehicle extends BaseDeferred<Vehicle> {
}

此时TypeScript会说:“嘿,DeferredPerson没有正确实现 BaseDeferred<Person> ,它缺少 age: DeferredProperty<number>name: DeferredProperty<string> 。(对于DeferredVehicle也是如此)
然而,目前这还不允许,因为我们不能说: abstract class BaseDeferred<T> implements Deferred<T>
这是一个可以理解的限制。毕竟,编译器如何实际验证 BaseDeferred<T> 正在实现 Deferred<T> ,当它无法知道(在这个时候)Deferred<T> 查找类型的扩展时。
虽然可以理解,但如果这个限制有可能对抽象类型进行解除的话就更好了。因为类型是抽象的,我们希望只有在类型具体派生时才进行检查。例如,当有人写:

class DeferredPerson extends BaseDeferred<Person> {
	// Now the compiler create the full type signature for BaseDeferred<Person> and then checked DeferredPerson against it.
}

--
今天的解决方法是这样做:

abstract class BaseDeferred<T> { // <-- note: no implements
}

class DeferredPerson extends BaseDeferred<Person> implements Deferred<Person> {
}
class DeferredVehicle extends BaseDeferred<Vehicle> implements Deferred<Vehicle> {
}

编译器现在适当地进行了正确的检查。这很不愉快,因为这是一个很容易错过的简单事情。由于所有子类都必须实现这种类型,我们非常希望将这一要求提高到基类,并将执行力统一应用于所有子类型。

非常感谢,我希望每个人都过得很好!我们在使用TS的过程中非常开心,尤其是(ab)利用类型系统表达一些非常有趣的东西。能够表达出更多疯狂的东西(特别是关于约束和可变参数类型),我们就越开心

drnojrws

drnojrws1#

@DanielRosenwasser。
这个(实际上)的原因是因为TS要求实现/扩展子句只允许接口(合理)。并且TS将SomeMappedType<SomeType>视为接口,如果Map类型已经被完全示例化(即它已经被展开,不再包含[p in keyof T]成员)。
描述请求的更简短方式是:允许抽象类包含此Map类型成员,然后在实际子类点而不是急切地解析该成员。

m2xkgtsf

m2xkgtsf2#

这里的常规建议是声明合并,例如:

class BaseDeferred<T> {
}
interface BaseDeferred<T> extends Deferred<T> { }

这是否给你想要的行为?

jjjwad0x

jjjwad0x3#

在我们的情况下,没有。尝试这个会产生"message: '一个接口只能扩展一个类或另一个接口。'"

同样的问题存在。也就是说,用类型参数示例化的Map类型不被视为接口(可以理解)。但如果能放宽这个限制就更好了。似乎Map类型是目前唯一需要经过额外的“解析真实成员”步骤的类型。如果接口也能具备这种能力,那就太棒了。

30byixjq

30byixjq4#

注意:如果今天确实存在这样的系统,我们当然会很高兴使用它。但是我的TS-fu对于所有这些新功能来说还不够强大,还没有弄清楚如何使这个系统正常工作。

xqk2d5yq

xqk2d5yq5#

有任何新闻吗?
我想做类似这样的事情

type IContext<T> = { [K in keyof T]: T[K] }

// Error: A class can only implement an object type or intersection of object types with statically known members.
class Context<T> implements IContext<T> { }

const ctx = new Context<{ id: number, name: string }>();

ctx.id
ctx.name
rseugnpd

rseugnpd6#

@xxxTonixxx仍然无法直接实现,我建议使用一个解决方法,使用一个单独的构造函数声明来返回适当的交集:

class _Context<T>  { /* members */ }

const Context: new<T>() => _Context<T> & T = _Context as any;
type Context<T> =  _Context<T> & T

const ctx = new Context<{ id: number, name: string }>();

ctx.id
ctx.name
wsxa1bj1

wsxa1bj17#

它起作用了!非常感谢,很好的技巧😸

whhtz7ly

whhtz7ly8#

关于这个有什么更新吗?

c8ib6hqw

c8ib6hqw9#

刚好偶然发现了这个确切的使用案例...

相关问题