2017-11-25 130 views
1

我想定义不同的数字类型,让我无法将它们意外,混合起来就像这样:如何在打字稿中创建不同的数字类型?

let h = 0 as Height; 
let w = 0 as Width; 

// These shall panic 
h === w; 
h + w; 

// These shall work 
(h as number) === (w as number); 
(h as number) + (w as number); 

类型别名不工作,因为他们仅仅是为了方便,并且被视为相同类型:

// This definition works but does not enforce type checking over just 
// using 'number' 
type Height = number; 
type Width = number; 

有关如何解决此问题的任何想法?

编辑:补充规定,即(h + w)将失败过

+0

你可以像'type Height = number&{__h:never}一样疯狂地进行破解| {__h:从不};键入Width = number&{__w:never} | {__w:从不}},但是这也会阻止'h + h;' –

回答

0

您可以指定类型,不同类型的号码的唯一方法是在嘲弄一个接口和一个独特的领域分配到的接口。

interface IHeight { height: any } 
interface IWidth { width: any } 

declare type Width = number & IWidth; 
declare type Height = number & IHeight; 

let h = 0 as Height; 
let w = 0 as Width; 

现在你不能没有它再次铸造数量做h = w直接。

+0

谢谢你的回答谢恩!这是非常有说服力的,严格来说,它完全适用于这个问题。但是,虽然构建失败,但其他运算符(如“h + w”)被允许,这限制了此方法的有用性。我会说这是一个50%的解决方案, 肯定比使用'号码更好。 – zupa

+0

打字稿中没有办法限制该行为,因为javascript允许算术运算符使用所有基元类型。你可以做到这一点的唯一方法是使用外部linting程序并设置一个自定义的lint规则。 [TSLint](https://github.com/palantir/tslint)允许自定义规则,您可以在此添加自定义类型到添加运算符规则。 –

0

这可能是矫枉过正,但我​​使用类来防止意外等价,例如与实体ID。我也使用类来封装单元 - 所以这既能保证类型安全,又能让您拥有单位转换的合理归宿。

class Width { 
    constructor(private value: number) { } 

    get pixels() { 
    return this.value; 
    } 
} 

class Height { 
    constructor(private value: number) { } 

    get pixels() { 
    return this.value; 
    } 
} 

这假定您使用标准单位创建尺寸。你可以添加额外的属性访问器来获取不同单位的高度(如CM,英寸等)。

当您使用WidthHeight它们互不兼容,所以你不能混合起来:

function getArea(width: Width, height: Height) { 
    return width.pixels * height.pixels; 
} 

// number not assignable to `Width` 
getArea(3, 4); 

// `Height` is not assignable to `Width` 
getArea(new Height(2), new Width(4)); 

// `Width` is not assignable to `Height` 
getArea(new Width(2), new Width(4)); 

// Ok 
getArea(new Width(2), new Height(4)); 

在大多数情况下,你会使用这个最终到终端的类型安全(就好像你在飞行中创建一个new Width(5) ......谁是说5是宽度 - 所以你试图阻止的错误仍然会在蠕变中)。所以在大多数情况下,您会从返回WidthHeight的东西中获得值,并且会阻止传统的...... obj.Heigl号码意外地作为高度传递。

const sizes = new SizeProvider(); 

// No 
getArea(sizes.Height, sizes.Width); 

// Ok 
getArea(sizes.Width, sizes.Height); 
+0

谢谢芬顿!我正在寻找替代方法,因为这也失败了'a + b',迫使人们使用'a.pixel()+ b.pixel()',但如果'a'和'b'是不同的类型, ,我们又失去了安全类型。 (例如,它们是传入的对象的属性。)所以我觉得这种方法与Shane提出的方法具有相同的实用性,但我发现另一种方法更简单。 – zupa