【问题标题】:Is there a way to represent a non-negative integer in TypeScript so that the compiler would prevent using fractions and negatives?有没有办法在 TypeScript 中表示非负整数,以便编译器防止使用分数和负数?
【发布时间】:2014-01-20 01:30:41
【问题描述】:

这个数字是一个非常松散的表示,有时需要收紧。就我而言,我希望一个变量只能采用非负整数。有没有办法在 TypeScript 中强制执行此约束?

【问题讨论】:

  • 不,TypeScript 或 JavaScript 中没有类似的东西。您需要以不同的方式强制执行它(例如使用保护值集的函数或属性)。

标签: typescript type-safety


【解决方案1】:

2021 年更新:

是的,模板文字允许这样做;观察:

type NonNegativeInteger<T extends number> =
    number extends T 
        ? never 
        : `${T}` extends `-${string}` | `${string}.${string}`
            ? never 
            : T;

请注意,number extends T 是限制通用 number 类型所必需的。

用法:

function negate<N extends number>(n: NonNegativeInteger<N>): number {
    return -n;
}


negate(3); // success
negate(3.1); // failure
negate(-3); // failure
negate(-3.1); // failure

用法

回应@ianstarz 评论:

您将如何在类字段或变量类型上使用它? myField: NonNegativeInteger = 42 似乎不起作用——我不确定在这种情况下作为泛型类型传入什么。您能否提供一个在这种情况下如何使用泛型的示例?

了解在 Typescript 中,文字本身被视为类型;例如:10 可分配给某些let x: number,但只有10 可分配给某些let x: 10。此外,Typescript 有一个强大的类型推断系统,但它只能走这么远才成为开发的负担。上述类型的目标是做以下两件事之一:

  1. 限制函数的文字参数。
  2. 应用进一步的类型操作。

您的问题不仅适用于类字段,也不适用于上述类型。 Typescript 变量在声明时应用类型推断,而不是赋值;这种推论不会扩展到变量上的泛型。

为了演示泛型变量类型和函数调用之间的区别,请考虑以下使用泛型标识类型时的错误

type Identity<T> = Identity;

// Generic type 'Example' requires 1 type argument(s)
let x: Identity = 10;

相比:

type Identity<T> = Identity;

function identity<T>(x: Identity<T>): T {
    return x;
}

let y = identity(10); // Success, y has type `number`
const z = identity(10); // Success, z has type `10`

注意z 是如何假定文字类型的。事实上,我们可以显式输入 y 相同的值,但它只允许将 10 作为一个值,而不是任何其他数字。

有限文字并集

如果您有有限数量的整数值,例如文件描述符,请创建一个类型如下的字段:

type EvenDigit = 0 | 2 | 4 | 6 | 8;

let x: EvenDigit = 2; // Success
let y: EvenDigit = 10; // Failure

如果你疯了,写一个脚本来生成联合类型。请注意,联合类型的成员数量可能有版本特定上限。

计算的文字并集

如果您想使用 SUPER meta 类似这样的内容会生成一系列类型:

// Assumes, for simplicity, that arguments Start and End are integers, and
// 0 < Start < End.
// Examples:
// Range<0, 5> -> 0 | 1 | 2 | 3 | 4 | 5
// Only can calculate so much:
// Range<0, 100> -> 'Type instantiation is excessively deep and possibly infinite.ts(2589)'
// Tail end recursion being introduced in Typescript 4.5 may improve this.
type Range<Start extends number, End extends number> = RangeImpl<Start, End>;
type RangeImpl<
    Start extends number,
    End extends number,
    T extends void[] = Tuple<void, Start>
> = End extends T["length"]
    ? End
    : T["length"] | RangeImpl<Start, End, [void, ...T]>;

// Helper type for creating `N` length tuples. Assumes `N` is an integer
// greater than `0`. Example:
// Tuple<number, 2 | 4> -> [number, number] | [number, number, number, number]
type Tuple<T, N extends number> = TupleImpl<T, N>;
// prettier-ignore
type TupleImpl<T, N extends number, U extends T[] = []> =
    N extends U["length"]
        ? U
        : TupleImpl<T, N, [T, ...U]>;

通用赋值方法

您可以创建一个具有赋值和检索器方法的类(不是 getter/setter 对,因为 An accessor cannot have type parameters ts(1094) )。

例子:

class MyClass {
    private _n: number = 42;
    
    // infers return type `number`
    getN() {
        return this._n;
    }

    setN<T>(n: NonNegativeInteger<T>) {
        // Optionally error check:
        if (Number.isInteger(n) || n <= 0) {
            throw new Error();
        }
        this._n = value;
    }
}

【讨论】:

  • Wowie zowie,打字不错!我期待有一天能在新闻中认出你的名字!
  • 如何在类字段或变量类型上使用它? myField: NonNegativeInteger&lt;number&gt; = 42 似乎不起作用——我不确定在这种情况下作为泛型类型传入什么。您能否提供一个在这种情况下如何使用泛型的示例?
  • 这只支持文字类型的值:negate(4/2)negate(1+1) 不起作用
  • @Yukulélé 那是因为这些表达式解析为输入“数字”。
  • 我不认为“数字扩展 T ?从不:...”是必要的,因为您在声明 中指定它。
【解决方案2】:

不,这是不可能的; JavaScript 中没有* uint 或类似的,所以 TypeScript 中没有对应的类型。有一个 open feature request for Contracts 可以让您提供更强大的断言,如果它曾经实现的话。

* Typed Array specification 中存在此类数据类型,但这些是主要为 WebGL 设计的扩展,不是核心语言的一部分。

【讨论】:

【解决方案3】:

@CSnover 的答案现在已过时为 the feature request has been rejected

但确实如 CSnover 所说,这在 TypeScript 中是不可能的。

This Babel plugin 可能是您可能拥有的最接近的方法。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2010-10-16
    • 1970-01-01
    • 2020-06-14
    • 1970-01-01
    • 2016-07-13
    • 1970-01-01
    • 1970-01-01
    • 2015-10-10
    相关资源
    最近更新 更多