TypeScript 中最接近 newtype 的方法是创建一个新的“名义”类型(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
类型Height 和Weight(对不起,我不能给新类型一个小写的名称)被编译器视为不同的类型。 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;
现在您可以使用不同的命名类型,这样您就不会在需要Weight 的地方意外使用Height,反之亦然。但是,就像newtype 一样,编译器不会将它们视为number。是的,您可以将Height 和Weight 设为number 的子类型(通过交集类型),但这可能是一个错误:像+ 这样的算术运算符能够对number 值进行操作,如果@987654347 @ 和w 是number 的子类型,那么h + w 不会出错。如果h 和w 不是number 的子类型,那么h + h 将是一个错误。而且您无法更改它,因为 TypeScript 不允许您更改运算符 the way it does with functions 的类型声明。我更喜欢阻止h + h 和h + w 编译,所以Height 和Weight 类型不是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;
}
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:
declare function measureScale(): Weight;
var a = height(68);
var b = measureScale();
并验证预期的结果:
console.log(add(a,b)); // err
console.log(add(a,a)); // okay
希望有所帮助;祝你好运!