为了避免将我输入的所有变量相互混淆,我想通过键入它们来添加额外的安全层,如果我试图将文字数字或另一种类型赋给这个新类型,就会产生编译错误.

你可以把它看作给我的变量一个单位,例如"秒"和"米".它们都是从文字类型"Numbers"派生的,但是我应该不能用一个类型的Meter添加一个变量类型的Second.我也不应该能够将类型为Second的变量赋给另一个类型为Meter的变量.

let length1   =  5 as Meter;
let length2   =  7 as Meter;
let duration1 = 10 as Second;
let duration2 = 11 as Second;

let lengthSum = length1 + length2         // should type lengthSum as Meter and give it a value of 12
let durationSum = duration1 + duration2   // should type durationSum as Second and give it a value of 21

length1 = duration2                // should generate an error
let foo = length1 + duration1;     // should generate an error

通过扩展,我也想对字符串执行同样的操作:

let myBookTitle = "Best title ever" as Title;
let myBookAuthor = "John Doe" as Author;

myBookTitle = myBookAuthor // should generate an error

.

我try 了以下自定义类型:

type Meter = number & { __type: 'meter' };
type Second = number & { __type: 'second' };

它们适用于大多数情况,但忽略了混合加法的情况:

let length1 = 5 as Meter;
let length2 = 10 as Meter;
let duration1 = 2 as Second;
let duration2 = 3 as Second;

length1 = length2; // works as expected
length1 = duration1; // generates an error as expected

let length10 = (length1 + length2) as Meter; // works but is not able to automatically infer the length10 type with the "as Meter"
let length11 = (duration1 + duration2) as Second; //same as above

let length12 = (length1 + duration2) as Second; // Shouldn't allow such addition but does it without any error!!

Would anyone know how to create a new type that covers all my constraints? (I know there are some npm packages that cover specifically the SI units, but that is not what I am looking for. Units are used here as examples; I will need to handle multiple types of différents types outside "simple" units. Thanks.)

推荐答案

不幸的是,这目前不可能如所要求的那样.像the addition operator (+)这样的原生JavaScript操作符的类型脚本行为已经嵌入到该语言中,您不能覆盖或定制它.因此,如果您编写x + y,其中xynumber的某个子类型,您将得到一个纯number,undo撤消您应用的任何nominal-like type branding.

microsoft/TypeScript#42218有一个开放的功能请求,基本上能够为这些操作符提供merge个您自己的overload个签名,但在实现这样的功能之前,没有办法做到这一点.


目前,只有变通的办法.您使用的特定解决方法可能超出了问题的范围,但以下是一些可能有用的起点:

您不能自定义运算符行为,但可以can自定义function行为,因此如果需要,可以将运算符包装在函数中,然后使用函数:

function add<T extends number>(t1: T, t2: T): T {
    return t1 + t2 as T;
}

let length10 = add(length1, length2);
//  ^? let length10: Meter
let length11 = add(duration1, duration2);
//  ^? let length11: Second
let length12 = add(length1, duration2); // error

显然,这并不理想,因为您需要记住使用add()而不是+.

或者,您可以重构以使用包含数字值的类,而不是直接使用数字:

class BrandedNumber<T extends string> {
    constructor(public __type: T, public value: number) { }
    add(other: BrandedNumber<T>) {
        return new BrandedNumber(this.__type, this.value + other.value);
    }
}
const Meter = (value: number) => new BrandedNumber("meter", value);
const Second = (value: number) => new BrandedNumber("second", value);
let length1 = Meter(5);
let length2 = Meter(10);
let duration1 = Second(2);
let duration2 = Second(3);
length1 = length2; // okay
length1 = duration1; // error
let length10 = length1.add(length2);
//  ^? let length10: BrandedNumber<"meter">
let length11 = duration1.add(duration2);
//  ^? let length11: BrandedNumber<"second">
let length12 = length1.add(duration2); // error

这也不是很理想,特别是如果您关心保留运行时行为(想必您不会想要进行任何像这样包装和展开数字的计算密集型处理).

同样,您 Select 的特定解决方案取决于您的用例,并且不在本文的讨论范围内;主要的观点是,如果没有操作员重载,则需要解决方案.

Playground link to code

Typescript相关问答推荐

如何传递基于TypScript中可变类型的类型?

参数类型undefined不能分配给参数类型字符串|未定义

有没有可能使用redux工具包的中间件同时监听状态的变化和操作

typescribe不能使用值来索引对象类型,该对象类型满足具有该值的另一个类型'

单击并移除for循环中的一项也会影响另一项

根据上一个参数值查找参数类型,也返回类型

等待用户在Angular 函数中输入

为什么&;(交集)运算符会删除不相交的属性?

与toast.error一起react 中的Async TUNK错误消息

如何解决&Quot;类型不可分配给TypeScrip IF语句中的类型&Quot;?

两个名称不同的相同打字界面-如何使其干燥/避免重复?

基于闭包类型缩小泛型类型脚本函数的范围

内联类型断言的工作原理类似于TypeScrip中的断言函数?

基于属性值的条件类型

是否可以将类型参数约束为不具有属性?

我如何键入它,以便具有字符串或数字构造函数的数组可以作为字符串或数字键入s或n

React Context TypeScript错误:属性';firstName';在类型';iCards|iSearch';

Svelte+EsBuild+Deno-未捕获类型错误:无法读取未定义的属性(读取';$$';)

必须从注入上下文调用Angular ResolveFn-inject()

在对象类型的类型别名中,属性是用分号 (;) 还是逗号 (,) 分隔的?