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

时间:2018-01-02 02:01:16

标签: typescript typing

我一直试图弄清楚如何在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.

有没有办法创建两种类型的数字,但彼此不兼容?

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

EDIT2:经过几个小时用棘尖的棍子戳问题后,我设法得到了一个答案,我在下面发布了这个答案。

2 个答案:

答案 0 :(得分:1)

TypeScript中与newtype最接近的是创建一个新的"名义上的" type(TypeScript没有标称类型,但有像branding这样的解决方法)并创建一个值构造函数和一个字段访问器函数,它只在实现中使用类型断言。例如:

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

类型HeightWeight(抱歉,不能让自己给新类型一个小写的名称)被编译器视为不同的类型。 height()函数是Height值构造函数(采用number并返回Height),inches()函数是其关联的字段访问器(采用Height并返回number),weight()pounds()Weight的类似函数。所有这些函数都只是运行时的标识函数。因此,JavaScript将它们视为具有一些函数调用开销的纯数字,并且希望通过一个好的编译器进行优化,如果你真的真的担心这个开销,你可以自己做断言:

const h = 12 as any as Height;
const w = 2000 as any as Weight;

现在您可以使用不同的命名类型,因此您不会意外地使用需要Height的{​​{1}},反之亦然。但是,就像Weight一样,编译器不会将它们视为newtype。是的,您可以number Height Weight的子类型(通过交叉点类型),但这可能是一个错误:像number这样的算术运算符能够要对+值进行操作,如果numberh都是w的子类型,那么number将不会出错。如果h + wh 不是 {/ 1}}的子类型,那么w将是一个错误。并且你无法改变它,因为TypeScript不允许你改变运算符the way it does with functions的类型声明。我希望阻止编译numberh + h,因此h + hh + w类型不是Height。相反,让我们创建我们自己的Weight函数,它的行为如你所愿:

number

add()函数接受 两个type Dimension = Height | Weight; function add<D extends Dimension>(a: D, b: D): D { return ((a as any) + (b as any)) as any; } 或两个add()参数,并返回相同类型的值。实际上,通过上述内容,它仍然可以传递Height作为Weight之类的内容,所以如果您真的认真地将其锁定,则可以使用重载:

Height | Weight

看哪:

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; } 功能,您只需将返回类型声明为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

measureScale()

验证预期结果:

Weight

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

答案 1 :(得分:-1)

所以,在我的头撞墙几个小时之后,我设法得到了这个:

class height extends Number {}
class weight extends Number {}

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

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

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

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

console.log(a+a); // Should NOT be an error.