假设我有两种我正在跟踪的数字,比如纬度
和经度
。我想用基本的数字
原语来表示这些变量,但不允许在typescript中将经度
赋值给纬度
变量。
有没有办法对数字
原语进行子分类,以便typescript检测到该赋值是非法的?以某种方式强制名义键入,使此代码失败?
var longitude : LongitudeNumber = new LongitudeNumber();
var latitude : LatitudeNumber;
latitude = longitude; // <-- type failure
“如何在typescript中扩展基元类型”的答案看起来这会让我走上正确的方向,但我不确定如何扩展该解决方案,为不同类型的数字创建不同的命名子类型。
我必须包装基元吗?如果是这样,我能让它像普通数字一样无缝地运行吗?或者我必须引用子成员?我可以创建一个打字稿编译时数字子类吗?
实际上,有一种方法可以实现您想要实现的目标,但它有点棘手,有一些限制,对于第一次看到该代码的人来说可能是完全不合理的,所以请将其视为一种好奇心,而不是实际的实现;)
好的,我们走吧。首先,我们需要创建一个Number
的“子类”。问题是,这个lib。d、 ts实际上将Number
声明为一个接口,而不是一个类(这是合理的-不需要实现方法,浏览器会处理这个问题)。因此,我们必须实现接口声明的所有方法,谢天谢地,我们可以使用已声明的varNumber
的现有实现。
class WrappedNumber implements Number {
//this will serve as a storage for actual number
private value: number;
constructor(arg?: number) {
this.value = arg;
}
//and these are the methods needed by Number interface
toString(radix?: number): string {
return Number.prototype.toString.apply(this.value, arguments);
}
toFixed(fractionDigits?: number): string {
return Number.prototype.toFixed.apply(this.value, arguments);
}
toExponential(fractionDigits?: number): string {
return Number.prototype.toExponential.apply(this.value, arguments);
}
toPrecision(precision: number): string {
return Number.prototype.toPrecision.apply(this.value, arguments);
}
//this method isn't actually declared by Number interface but it can be useful - we'll get to that
valueOf(): number {
return this.value;
}
}
现在,我们创建了一个类型WrappedNumber
,其行为与数字类型类似。您甚至可以添加两个WrappedNumber
s,这要归功于valueOf()
方法。但是,这里有两个限制:首先,您需要强制转换变量来执行此操作。第二:结果将是一个常规的数字
,因此之后应该再次包装它。让我们看一个加法的例子。
var x = new WrappedNumber(5);
var y = new WrappedNumber(7);
//We need to cast x and y to <any>, otherwise compiler
//won't allow you to add them
var z = <any>x + <any>y;
//Also, compiler now recognizes z as of type any.
//During runtime z would be a regular number, as
//we added two numbers. So instead, we have to wrap it again
var z = new WrappedNumber(<any>x + <any>y); //z is a WrappedNumber, which holds value 12 under the hood
在我看来,这是最棘手的部分。现在我们创建了两个类,Latitude
和Longitude
,它们将继承自WrappedNumber
(这样它们就可以作为数字)
class Latitude extends WrappedNumber {
private $;
}
class Longitude extends WrappedNumber {
private $;
}
搞什么鬼?TypeScript在比较类型时使用duck类型。这意味着,当两种不同类型具有相同的属性集时,它们被视为“兼容”(因此可分配给其自身,即您可以将一种类型的变量分配给另一种类型的值)。解决方案非常简单:添加一个私有成员。这个私有成员是纯虚拟的,它不在任何地方使用,也不会被编译。但它使TypeScript认为纬度
和经度
是完全不同的类型,我们更感兴趣的是,不允许将经度
类型的变量分配给纬度
类型的变量。
var a = new Latitude(4);
var b: Longitude;
b = a; //error! Cannot convert type 'Latitude' to 'Longitude'
瞧!这就是我们想要的。但是代码很混乱,您需要记住强制转换类型,这非常不方便,所以实际上不要使用它。然而,正如你所看到的,这是可能的。
以下是实现这一目标的简单方法:
您只需要两个函数,一个用于将数字转换为数字类型,另一个用于反向处理。以下是两个功能:
module NumberType {
/**
* Use this function to convert to a number type from a number primitive.
* @param n a number primitive
* @returns a number type that represents the number primitive
*/
export function to<T extends Number>(n : number) : T {
return (<any> n);
}
/**
* Use this function to convert a number type back to a number primitive.
* @param nt a number type
* @returns the number primitive that is represented by the number type
*/
export function from<T extends Number>(nt : T) : number {
return (<any> nt);
}
}
您可以创建自己的数字类型,就像这样:
interface LatitudeNumber extends Number {
// some property to structurally differentiate MyIdentifier
// from other number types is needed due to typescript's structural
// typing. Since this is an interface I suggest you reuse the name
// of the interface, like so:
LatitudeNumber;
}
下面是一个如何使用LatitudeNumber的例子
function doArithmeticAndLog(lat : LatitudeNumber) {
console.log(NumberType.from(lat) * 2);
}
doArithmeticAndLog(NumberType.to<LatitudeNumber>(100));
这将日志200
到控制台。
如您所料,此函数不能使用数字原语或其他数字类型调用:
interface LongitudeNumber extends Number {
LongitudeNumber;
}
doArithmeticAndLog(2); // compile error: (number != LongitudeNumber)
doArithmeticAndLog(NumberType.to<LongitudeNumber>(2)); // compile error: LongitudeNumer != LatitudeNumber
这只不过是愚弄Typescript,使其相信一个原语数字实际上是数字接口的某个扩展(我称之为数字类型),而实际上原语数字永远不会转换为实现数字类型的实际对象。不需要转换,因为数字类型的行为类似于原始数字类型;数字类型只是一个数字原语。
诀窍是简单地转换为any
,以便typecript停止类型检查。所以上面的代码可以改写为:
function doArithmeticAndLog(lat : LatitudeNumber) {
console.log(<any> lat * 2);
}
doArithmeticAndLog(<any>100);
正如您所见,函数调用甚至不是真正必要的,因为数字及其数字类型可以互换使用。这意味着在运行时需要产生绝对零的性能或内存损失。我仍然强烈建议使用函数调用,因为函数调用的成本几乎为零,并且通过强制转换到any
您自己,您就失去了类型安全性(例如doarithmetricandlog(
它还可以用于其他基本体,如字符串和布尔值。
打字快乐!
您可以使用助手类型近似Typescript中的不透明/标称类型。有关更多详细信息,请参阅此答案:
// Helper for generating Opaque types.
type Opaque<T, K> = T & { __opaque__: K };
// 2 opaque types created with the helper
type Int = Opaque<number, 'Int'>;
type ID = Opaque<number, 'ID'>;
// works
const x: Int = 1 as Int;
const y: ID = 5 as ID;
const z = x + y;
// doesn't work
const a: Int = 1;
const b: Int = x;
// also works so beware
const f: Int = 1.15 as Int;
下面是一个更详细的答案:https://stackoverflow.com/a/50521248/20489
还有一篇关于不同方法的好文章:https://michalzalecki.com/nominal-typing-in-typescript/
我想创建如下界面: 然后我们可以将它用于一个新类: 我想对s做同样的事情。在纯JavaScript中,我可以使类型,例如,满足以下接口: 但TypeScript不允许我这样做: 有办法做到这一点吗?
我有一个非常简单的练习,我注册客户端名称(字符串),我使用数组来做到这一点,所以当我添加一个新的我将使用另一个辅助数组,然后增加原来的长度,或者我将使用System.array复制,有没有其他方法,逐渐增加数组长度,因为你需要添加元素到该数组,不涉及辅助数组?
问题内容: 假设我有以下情况: 有没有一种方法可以保证实现的任何类也必须扩展?我不想创建一个抽象类,因为我希望能够以类似的方式混合其他一些接口。 例如: 问题答案: Java接口无法扩展类,这很有意义,因为类包含无法在接口内指定的实现细节。 解决此问题的正确方法是通过将接口也完全从实现中分离出来。所述等可以扩展接口以迫使程序员来实现相应的方法。如果要在所有实例之间共享代码,则可以将(可能是抽象的)
假设有一个类库X的输入文件,其中包含一些接口。 为了使用这个库,我需要传递一个与。当然,我可以在源文件中创建相同的接口: 但这样一来,我就有了让它和库中的一个保持最新的负担,而且它可能非常大,并导致大量代码重复。 因此,有没有办法“提取”接口的这个特定属性的类型?类似于(这不起作用,导致“找不到名称I2”错误)。 编辑:在TS操场上玩了一会儿后,我注意到以下代码完全实现了我想要的: 但是,它需要声
我在typescript中有以下泛型类 但是我不知道为什么得到这个错误Class'(匿名类)'不正确地扩展基类'列'。属性getValue的类型不兼容。类型'(值:数字)=
我有一个打字错误310.4。1在Ubuntu20.04上安装(composer模式),使用PHP7.4和mariadb数据库,并安装扩展名“extension Builder”(v10兼容性)。当我使用扩展生成器使用域模型创建一个新的扩展并保存它时,将其添加到编写器时,也不会出现错误。json并运行composerrequire命令。但是,数据库中没有创建表。 有人知道从哪里开始寻找问题吗? 提前