如何在TypeScript中创建2个不兼容的类似数字类型?

Sta*_*cer 9 typing typescript

我一直试图弄清楚如何在TS中创建2个互不兼容的数字/整数类型.

例如,在下面的代码中,高度和重量都是数字,但将它们加在一起或等效处理它们的概念是没有意义的,应该是一个错误.

type height = number; // inches
type weight = number; // pounds
var a: height = 68;
var b: weight = operateScale(); // Call to external, non-TS function, which returns a weight.
console.log(a+b); // Should be an error.
Run Code Online (Sandbox Code Playgroud)

有没有办法创建2个类型,这两个类型都是数字,但彼此不兼容?


编辑:正如评论部分所述,这种行为似乎与haskell的newtype行为类似.


编辑2:经过几个小时用尖尖的棍子戳问题,我设法得到了答案,我在下面发布了.

jca*_*alz 6

newtype与 TypeScript 中的a 最接近的是创建一个新的“名义”类型(TypeScript 没有名义类型,但有诸如品牌之类的解决方法)并创建一个值构造函数和一个字段访问器函数,该函数仅在实现中使用类型断言。例如:

interface Height { 
  __brand: "height"
}
function height(inches: number): Height {
  return inches as any;
}
function inches(height: Height): number {
  return height as any;
}

interface Weight { 
  __brand: "weight"
}
function weight(pounds: number): Weight {
  return pounds as any;
}
function pounds(weight: Weight): number {
  return weight as any;
}

const h = height(12); // one foot
const w = weight(2000); // one ton
Run Code Online (Sandbox Code Playgroud)

类型HeightWeight(抱歉,我无法为新类型指定小写名称)被编译器视为不同的类型。该height()函数是一个Height值构造函数(接受 anumber并返回 a Height),该inches()函数是其关联的字段访问器(接受 aHeight并返回 a number),weight()并且pounds()是 的类似函数Weight。所有这些函数在运行时都只是身份函数。因此,JavaScript 将它们视为纯数字,并带有一些函数调用开销,希望能被好的编译器优化掉,如果您真的担心这种开销,您可以自己进行断言:

const h = 12 as any as Height;
const w = 2000 as any as Weight;
Run Code Online (Sandbox Code Playgroud)

现在,您可以使用不同的命名类型,因此您不会意外地HeightWeight需要a 的地方使用 a ,反之亦然。但是,就像 a 一样newtype,编译器不会将它们视为number. 是的,您可以创建Height和 的Weight子类型number(通过交集类型),但这可能是一个错误:算术运算符 like+能够对number值进行操作,如果两者都是hw的子类型number,则h + w不会出错。而如果hw不是亚型number,那么h + h将是一个错误。你不能改变它,因为 TypeScript 不允许你改变运算符的类型声明将其与功能的方式做。我更喜欢同时阻止h + handh + w编译,所以HeightandWeight类型不是numbers。相反,让我们创建自己的add()函数来按照您的意愿行事:

type Dimension = Height | Weight;

function add<D extends Dimension>(a: D, b: D): D {
  return ((a as any) + (b as any)) as any;
}
Run Code Online (Sandbox Code Playgroud)

所述add()函数接受或者2个Height或两个Weight参数,并返回相同的类型的值。实际上,通过上面的内容,仍然可以传入Height | Weightas 之类的内容D,因此如果您真的很想将其锁定,则可以改用重载:

function add(a: Height, b: Height): Height;
function add(a: Weight, b: Weight): Weight;
function add(a: any, b: any): any {
  return a+b;
}
Run Code Online (Sandbox Code Playgroud)

而且,看哪:

const twoH = add(h, h); // twoH is a Height
const twoW = add(w, w); // twoW is a Weight
const blah = add(h, w); // error, Height and Weight don't mix
Run Code Online (Sandbox Code Playgroud)

所以我们差不多完成了。对于您的外部measureScale()函数,您只需将返回类型声明为 a Weight

declare function measureScale(): Weight;

var a = height(68);
var b = measureScale();
Run Code Online (Sandbox Code Playgroud)

并验证预期结果:

console.log(add(a,b)); // err
console.log(add(a,a)); // okay
Run Code Online (Sandbox Code Playgroud)

希望有所帮助;祝你好运!


Sta*_*cer 1

所以,在我把头撞在墙上几个小时之后,我设法想出了这个:

class height extends Number {}
class weight extends Number {}
Run Code Online (Sandbox Code Playgroud)

通过对类进行子类化Number,Typescript 允许您创建不同的数字类型。

然后您可以使用上面指定的变量。

var a: height = 68;
var b: weight = 184;
console.log(a+b); // Should be an error.
Run Code Online (Sandbox Code Playgroud)

我遇到的问题是这也会返回一个错误:

console.log(a+a); // Should NOT be an error.
Run Code Online (Sandbox Code Playgroud)