为什么下面的 TypeScript 程序不会抛出类型错误?
Why doesn't the following TypeScript program throw a type error?
interface Eq<A> {
eq(this: A, that: A): boolean;
};
class Pair<A> implements Eq<Pair<A>> {
constructor(public x: A, public y: A) {}
eq(this: Pair<A>, that: Pair<A>): boolean {
return this.x === that.x && this.y === that.y;
}
}
class Triple<A> implements Eq<Triple<A>> {
constructor(public x: A, public y: A, public z: A) {}
eq(this: Triple<A>, that: Triple<A>): boolean {
return this.x === that.x && this.y === that.y && this.z === that.z;
}
}
const eq = <A extends Eq<A>>(x: A, y: A): boolean => x.eq(y);
console.log(eq(new Pair(1, 2), new Triple(1, 2, 3)));
console.log(eq(new Triple(1, 2, 3), new Pair(1, 2)));
我原以为 TypeScript 编译器会抱怨最后两行,因为您不应该将 eq
函数应用于两个不同类型的值。但是,TypeScript 编译器不会为上述程序抛出任何类型错误。上述程序的结果是 true
和 false
.
为什么 TypeScript 编译器不会为上述程序抛出类型错误?我们怎样才能让它正确捕获这些类型的错误?
由于 TypeScript 中使用了 structural subtyping,程序得以编译(与其他编程语言中经常出现的名义子类型相反)。
请注意,您的 Triple
class 是一个可以分配给类型 Pair
:
的变量
const p: Pair<number> = new Triple(1, 2, 3);
在你的例子中:
console.log(eq(new Pair(1, 2), new Triple(1, 2, 3)));
console.log(eq(new Triple(1, 2, 3), new Pair(1, 2)));
eq
的类型推断为:
const eq: <Pair<number>>(x: Pair<number>, y: Pair<number>) => boolean
如上所示,Triple
是 Pair
类型参数的有效参数,因此一切都可以干净地编译。
您可以将不同的私有字段添加到您的 classes 以模拟名义子类型化。在此特定示例中,您可以选择以下两个选项之一:
- 添加额外的标记字段
- 将
x
、y
、z
设为私有并提供吸气剂
见
首先我投票给@VLAZ 的评论。
由于 Pair 和 Triple 都实现了 Eq
,我相信这就是您没有编译错误的原因。
此外,根据@VLAZ 的评论,TS 不支持更高种类的类型。
既然你使用的是 F-bounded 多态性,我相信你应该给 TS 一个关于第二个参数的提示。
interface Eq<A> {
eq(this: A, that: A): boolean;
};
class Pair<A> implements Eq<Pair<A>> {
constructor(public x: A, public y: A) { }
eq(this: Pair<A>, that: Pair<A>): boolean {
return this.x === that.x && this.y === that.y;
}
}
class Triple<A> implements Eq<Triple<A>> {
constructor(public x: A, public y: A, public z: A) { }
eq(this: Triple<A>, that: Triple<A>): boolean {
return this.x === that.x && this.y === that.y && this.z === that.z;
}
}
// hint is here :D
const eq = <Fst extends Eq<Fst>, Scd extends Eq<Scd> & Fst>(x: Fst, y: Scd): boolean => x.eq(y);
const x = eq(new Pair(1, 2), new Triple(1, 2, 3)); // ok
const y = eq(new Triple(1, 2, 3), new Pair(1, 2)); // error
这不是一个完整的答案,但我希望它能给你一些线索
interface Eq<A> {
eq(this: A, that: A): boolean;
};
class Pair<A> implements Eq<Pair<A>> {
constructor(public x: A, public y: A) {}
eq(this: Pair<A>, that: Pair<A>): boolean {
return this.x === that.x && this.y === that.y;
}
}
class Triple<A> implements Eq<Triple<A>> {
constructor(public x: A, public y: A, public z: A) {}
eq(this: Triple<A>, that: Triple<A>): boolean {
return this.x === that.x && this.y === that.y && this.z === that.z;
}
}
const eq = <A extends Eq<A>>(x: A, y: A): boolean => x.eq(y);
console.log(eq(new Pair(1, 2), new Triple(1, 2, 3)));
console.log(eq(new Triple(1, 2, 3), new Pair(1, 2)));
我原以为 TypeScript 编译器会抱怨最后两行,因为您不应该将 eq
函数应用于两个不同类型的值。但是,TypeScript 编译器不会为上述程序抛出任何类型错误。上述程序的结果是 true
和 false
.
为什么 TypeScript 编译器不会为上述程序抛出类型错误?我们怎样才能让它正确捕获这些类型的错误?
由于 TypeScript 中使用了 structural subtyping,程序得以编译(与其他编程语言中经常出现的名义子类型相反)。
请注意,您的 Triple
class 是一个可以分配给类型 Pair
:
const p: Pair<number> = new Triple(1, 2, 3);
在你的例子中:
console.log(eq(new Pair(1, 2), new Triple(1, 2, 3)));
console.log(eq(new Triple(1, 2, 3), new Pair(1, 2)));
eq
的类型推断为:
const eq: <Pair<number>>(x: Pair<number>, y: Pair<number>) => boolean
如上所示,Triple
是 Pair
类型参数的有效参数,因此一切都可以干净地编译。
您可以将不同的私有字段添加到您的 classes 以模拟名义子类型化。在此特定示例中,您可以选择以下两个选项之一:
- 添加额外的标记字段
- 将
x
、y
、z
设为私有并提供吸气剂
见
首先我投票给@VLAZ 的评论。
由于 Pair 和 Triple 都实现了 Eq
,我相信这就是您没有编译错误的原因。
此外,根据@VLAZ 的评论,TS 不支持更高种类的类型。
既然你使用的是 F-bounded 多态性,我相信你应该给 TS 一个关于第二个参数的提示。
interface Eq<A> {
eq(this: A, that: A): boolean;
};
class Pair<A> implements Eq<Pair<A>> {
constructor(public x: A, public y: A) { }
eq(this: Pair<A>, that: Pair<A>): boolean {
return this.x === that.x && this.y === that.y;
}
}
class Triple<A> implements Eq<Triple<A>> {
constructor(public x: A, public y: A, public z: A) { }
eq(this: Triple<A>, that: Triple<A>): boolean {
return this.x === that.x && this.y === that.y && this.z === that.z;
}
}
// hint is here :D
const eq = <Fst extends Eq<Fst>, Scd extends Eq<Scd> & Fst>(x: Fst, y: Scd): boolean => x.eq(y);
const x = eq(new Pair(1, 2), new Triple(1, 2, 3)); // ok
const y = eq(new Triple(1, 2, 3), new Pair(1, 2)); // error
这不是一个完整的答案,但我希望它能给你一些线索