gpt4 book ai didi

angular - 将类类型分配给泛型变量时,Typescript编译错误

转载 作者:太空狗 更新时间:2023-10-29 18:35:19 24 4
gpt4 key购买 nike

我试图在typescript中将类类型设置为泛型变量。使用泛型版本时显示编译错误。

export class A<T extends Al> { }

export class B extends A<Bl> { }

export class Al { }

export class Bl extends Al { }

show<T extends A<Al>>() {
let t: Type<T>; // "t = B" shows compile error but seem to work
// let t: Type<A<Al>>; // "t = B" shows no compile error and still works
t = B;
}

我还创建了一个stackblitz示例 here,这样您就可以看到它是如何工作的。
我希望如果 B extends A<Al>T extends A<Al>,通用版本 let t: Type<T>; t = B;可以工作。相反,我必须使用 let t: Type<A<Al>>; t = B;,我想知道我是否可以使用通用方法?
提前谢谢你。

最佳答案

这里的示例代码违反了几个typescript最佳实践,因此我很难看到您的实际用例是什么:
您创建的所有类型都等效于空类型{},因此都是structurally equivalent to each other。这在实践中是很少见的。即使这里的代码只是一个玩具示例,我也怀疑您所指的A<T>BAlBl是否完全相同,而且即使您解决了问题中所述的问题,也可能会导致意外行为。
泛型类A<T>does not depend structurally on its type parameter。因此,类型A<Al>A<Bl>完全相同,而A<string>A<string>完全相同(您是否认为string不可能,因为Al不扩展Al?是的,因为show()是空的…参见上一个要点)。再一次,我怀疑你的意思是这是真的。
因此,我将把您的示例代码修改为:

export class A<T extends Al> {
constructor(public t: T) {}
a: string = "a";
}

export class B extends A<Bl> {
constructor(t: Bl) {
super(t);
}
b: string = "b";
}

export class Al {
al: string = "al";
}

export class Bl extends Al {
bl: string = "bl";
}

现在,明显命名的类型实际上在结构上彼此不同,泛型类型依赖于它们的类型参数。这并不能解决您所问的问题,但它确实阻止了编译器在使用类型时以奇怪的方式运行。
还有一个我会注意但不会改变的:
T函数在 T中是通用的,但似乎不接受或返回任何依赖于 T类型的参数。这很好,因为它只是示例代码,但是编译器无法从 show()的用法推断 T(请参阅 the documentation中的“类型参数推断”)。调用时,我们只需手动指定 show<T>()。但在实际代码中,您可能希望 T的调用签名依赖于 T(或者您将从签名中完全删除 tGood)。
现在让我们看看发生了什么,并检查您得到的实际编译器错误:
function show<T extends A<Al>>() {
let tGood: Type<A<Al>> = B; // okay

let tBad: Type<T> = B; // error!
// 'B' is assignable to the constraint of type 'T',
// but 'T' could be instantiated with a different
// subtype of constraint 'A<Al>'.
}

所以,正如你所说, Type<A<Al>>是有效的。类型 A<Al>表示“可分配给 B的事物的构造函数”。值 Type<A<Al>>是一个完全有效的 B,因为它是一个构造函数,它生成 A<Al>实例,这些实例被显式定义为扩展 X
注意“ Y扩展 X”、“ Y可分配给 X”、“ YX窄(或与 Y相同)”都表示(大致)相同的事情:如果您有一个 X类型的值,则可以将其分配给 Y类型的变量。扩展性/可分配性/狭窄性不是对称的。如果 Y扩展 X,则 string扩展 string | number并不一定正确。例如,考虑 const sn: string | number = "x"可分配给 string | number(例如, string可以),但 const s: string = Math.random()<0.5 ? "x" : 1不可分配给 tBad(例如, T是一个错误)。
现在看看 T。这是一个错误,因为 A<Al>是某种泛型,我们只知道 A<Al>扩展了 A<Al>。它不一定等于 T。事实上,可能是一个严格意义上较窄的 A<Al>亚型(也就是说, A<Al>extends T并不意味着 Bextends Type<A<Al>>)。因此我们不能将 B分配给 T,因为 'T' could be instantiated with a different subtype of constraint 'A<Al>'的实例可能不会成为 C的实例。(错误消息明确指出: c
我们来举一个具体的例子:
export class C extends A<Al> {
constructor(t: Al) {
super(t);
}
c: string = "c";
}
show<C>(); // no error, but B does not create instances of C

您可以看到 B的一个实例有一个名为 c的字符串属性。但是 show<C>()构造函数不生成具有 T属性的实例。我可以调用 C,将 C指定为 A<Al>。这是可以接受的,因为 let t: Type<T> = B扩展了 let t: Type<C> = B,但是…在实现的内部, tGood实际上是在说 Type<T>,这是不正确的。因此产生了错误。
既然我不知道你想用示例代码做什么,我不知道该如何建议修复它。当然,你可以使用你的 B。完全删除泛型可以解决这个问题。如果需要继续使用泛型,则可能需要传入这些泛型类型的参数。也就是说,像这样:
function showMaybe<T extends A<Al>>(ctor: Type<T>) {
let tFine: Type<T> = ctor;
}

如果你需要一个 showUnsafe<C>()你必须通过一个…你不能只希望 tUnsafe是一个。再说一次,我不知道上面的方法对你是否有效,但像这样的方法可能是可行的。
如果您只是想让编译器警告保持沉默,并且确信您所做的实际上是安全的,那么您可以使用 type assertion
function showUnsafe<T extends A<Al>>() {
let tUnsafe: Type<T> = B as any as Type<T>; // unsafe assertion
}

这将不再产生错误,但与以前的问题相同。您仍然可以调用 C,根据您对 B所做的操作,您可能最终会对编译器撒谎,认为构造的实例是 类型的,而实际上是 类型的。当您使用类型断言时,您将类型安全的责任从编译器转移到您身上,因此,只有当断言结果为false并产生不愉快的运行时结果时,您才有责任责怪自己。
好吧,希望这有意义,对你有帮助。祝你好运!
Link to code

关于angular - 将类类型分配给泛型变量时,Typescript编译错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56933110/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com