是否可以将数量限制在一定范围内

ASD*_*rte 37 types typescript

由于typescript 2.0 RC(甚至是beta?),因此可以使用数字文字类型,如type t = 1 | 2;.是否可以将类型限制为数字范围,例如0-255,而无需在类型中写出256个数字?

在我的例子中,一个库接受0-255的调色板的颜色值,我更喜欢只列举一些,但将其限制为0-255:

const enum paletteColor {
  someColor = 25,
  someOtherColor = 133
}
declare function libraryFunc(color: paletteColor | 0-255); //would need to use 0|1|2|...
Run Code Online (Sandbox Code Playgroud)

Gui*_*tio 58

Typescript 4.5 可以对条件类型进行尾递归消除

type Enumerate<N extends number, Acc extends number[] = []> = Acc['length'] extends N
  ? Acc[number]
  : Enumerate<N, [...Acc, Acc['length']]>

type Range<F extends number, T extends number> = Exclude<Enumerate<T>, Enumerate<F>>

type T = Range<20, 100>
Run Code Online (Sandbox Code Playgroud)

  • 这是最好的答案,只需添加`| T` 就像 `排除&lt;Enumerate&lt;T&gt;, Enumerate&lt;F&gt;&gt; | T` 使这项工作正常工作 `const range : Range&lt;0, 100&gt; = 100;` (12认同)
  • 事实上,尾递归消除允许比其他解决方案所具有的 41 个递归调用多得多的递归调用,尽管没有完全消除它(例如 `Rangee&lt;10, 1000&gt;` 仍然失败)。PS:不会 `type ArrayToUnion&lt;T extendsknown[]&gt; = T extends (infer U)[] 吗?U:从来没有;`是一样的,但更简单? (3认同)
  • 事实上,我们可以做更简单的`type TupleToUnion&lt;T extendsknown[]&gt; = T[number]`。请参阅[元组到联合](https://github.com/type-challenges/type-challenges/blob/master/questions/10-medium-tuple-to-union/README.md)。 (2认同)

Ada*_*myd 36

如果你的范围很小,你总是可以这样写:

type MyRange = 5|6|7|8|9|10

let myVar:MyRange = 4; // oops, error :)
Run Code Online (Sandbox Code Playgroud)

当然它只适用于整数而且很丑陋:)

  • "没有在类型中写出256个数字" (25认同)
  • @Quentin2 不要编写,在控制台中生成一个字符串,然后复制/粘贴到您的代码... `new Array(256).fill(0).map((_, i) =&gt; i).join(" |“)` (2认同)

tit*_*sfx 26

是的,这是可能的,但是

第一个。解决方案将是一个肮脏的解决方案 2。解决方案将是部分的(从 x 到 y,其中 y 是一个小数,在我的情况下为 43) 第三个。解决方案将是一个完整的解决方案,但真正推进变压器、装饰器等。

1.使用@Adam-Szmyd解决方案的肮脏解决方案(最简单快速的方法):

type RangeType = 1 | 2 | 3
Run Code Online (Sandbox Code Playgroud)

如果您需要广泛的范围,只需打印和复制/粘贴:

type RangeType = 1 | 2 | 3
Run Code Online (Sandbox Code Playgroud)

你可能不愿意做这样的事情

const data = [1, 2, 4, 5, 6, 7] as const;
type P = typeof data[number];
Run Code Online (Sandbox Code Playgroud)

显示 P 具有枚举类型

而是使用函数

const rangeType20 = Array.from(Array(20).keys()) as const;
Run Code Online (Sandbox Code Playgroud)

表明不能用函数来完成

但目前这不起作用,只有在是文字时才有效。甚至错误也不完全正确。

2.部分解决方案(来源 部分解决方案

type PrependNextNum<A extends Array<unknown>> = A['length'] extends infer T ? ((t: T, ...a: A) => void) extends ((...x: infer X) => void) ? X : never : never;

type EnumerateInternal<A extends Array<unknown>, N extends number> = { 0: A, 1: EnumerateInternal<PrependNextNum<A>, N> }[N extends A['length'] ? 0 : 1];

export type Enumerate<N extends number> = EnumerateInternal<[], N> extends (infer E)[] ? E : never;

export type Range<FROM extends number, TO extends number> = Exclude<Enumerate<TO>, Enumerate<FROM>>;

type E1 = Enumerate<43>;

type E2 = Enumerate<10>;

type R1 = Range<0, 5>;

type R2 = Range<0, 43>;
Run Code Online (Sandbox Code Playgroud)

3.完整的解决方案,但真正与推进TransformersDecorators等等。

使用第一个解决方案中的函数,您可以compiletime使用转换器将 at 替换为值。同样,但runtime使用装饰器。

  • 值得注意的是,到目前为止,任何递归解决方案都仅限于固定的缩减深度,一旦达到,就会抛出“类型实例化过深且可能无限。(2589)”(只是向其他读者提及为什么“其中 y 是一个小数”约束) (3认同)

Dea*_* Xu 21

更新1

从 typescript v4.5 开始添加tail recursive evaluation of conditional types.问题链接

现在最大数量可以是998。这对于你的问题来说已经足够了。

游乐场链接

type Ran<T extends number> = number extends T ? number :_Range<T, []>;
type _Range<T extends number, R extends unknown[]> = R['length'] extends T ? R[number] : _Range<T, [R['length'], ...R]>;

type R5 = Ran<998>
const a: R5 = 3 // correct
const b: R5 = 999 // wrong
Run Code Online (Sandbox Code Playgroud)

来源答案

现在可以使用 Typescript 4.1递归条件类型

type Ran<T extends number> = number extends T ? number :_Range<T, []>;
type _Range<T extends number, R extends unknown[]> = R['length'] extends T ? R[number] : _Range<T, [R['length'], ...R]>;

type R5 = Ran<998>
const a: R5 = 3 // correct
const b: R5 = 999 // wrong
Run Code Online (Sandbox Code Playgroud)

但不幸的是,如果你的长度太长,递归类型就会失败

type Range<T extends number> = number extends T ? number :_Range<T, []>;
type _Range<T extends number, R extends unknown[]> = R['length'] extends T ? R['length'] : R['length'] | _Range<T, [T, ...R]>;

type R5 = Range<5>
const a: R5 = 3 // correct
const b: R5 = 8 // error. TS2322: Type '8' is not assignable to type '0 | 1 | 2 | 3 | 4 | 5'.
Run Code Online (Sandbox Code Playgroud)

嗯,它有效,但实际上并不有效。:)


Ale*_*exG 19

不,这是不可能的.打字稿中没有这种精确的类型约束(但是?)

只有运行时检查/断言才能实现:(

  • 使用 TypeScript 4.5.0 这将成为可能。请参阅第二个游乐场链接:/sf/answers/4836313051/ (2认同)

Dru*_*ney 11

目前尚不可能,但是GitHub上存在一个未解决的问题。目前,他们仍在等待提案,但有一天可能会使用此功能。

简而言之,除非提出建议,否则您将无法使用一系列数字作为类型。

  • 具体提案现在在这里:[间隔类型/不等式类型](https://github.com/microsoft/TypeScript/issues/43505) (4认同)

Gra*_*uxe 8

虽然不是最好的解决方案(因为一些检查将在运行时处理),但值得一提的是“不透明类型”可以帮助强制您输入预期值。

下面是一个例子:

type RGBColor = number & {_type_: "RGBColor"};

const rgb = (value: number): RGBColor => {
  if (value < 0 || value > 255) {
    throw new Error(`The value ${value} is not a valid color`);
  }

  return value as RGBColor;
};

// Compiler errors
const color1: RGBColor = 200; // fail - number is not RGBColor
const color2: RGBColor = 300; // fail - number is not RGBColor

// Runtime error
const color3: RGBColor = rgb(300); // fail - The value 300 is not a valid color

// Pass
const color4: RGBColor = rgb(100);
const color5: RGBColor = rgb(255);
Run Code Online (Sandbox Code Playgroud)


uda*_*rrr 5

带验证范围数字(正数和整数范围) ts 4.6.3

type IsPositive<N extends number> = `${N}` extends `-${string}` ? false : true;

type IsInteger<N extends number> = `${N}` extends `${string}.${string}`
  ? never
  : `${N}` extends `-${string}.${string}`
  ? never
  : number;

type IsValid<N extends number> = IsPositive<N> extends true
  ? IsInteger<N> extends number
    ? number
    : never
  : never;

type PositiveNumber<
  N extends number,
  T extends number[] = []
> = T["length"] extends N ? T[number] : PositiveNumber<N, [...T, T["length"]]>;

type Range<N1 extends IsValid<N1>, N2 extends IsValid<N2>> = Exclude<
  PositiveNumber<N2>,
  PositiveNumber<N1>
>;
type RangeType = Range<1, 5>;
Run Code Online (Sandbox Code Playgroud)

这里的范围为负,但有一些限制。范围是文字。我不知道为什么,但我无法得到不是字面上的负数。也许有人知道warraround

type IsInteger<N extends number> = `${N}` extends `${string}.${string}`
  ? never
  : `${N}` extends `-${string}.${string}`
  ? never
  : number;

type NegativeLiteralNumbers<
  N extends number,
  T extends string[] = []
> = `${N}` extends `-${string}`
  ? `-${T["length"]}` extends `${N}`
    ? T[number]
    : NegativeLiteralNumbers<N, [...T, `-${T["length"]}`]>
  : never;

type PositiveLiteralNumber<
  N extends number,
  T extends string[] = []
> = `${N}` extends `${string}`
  ? T["length"] extends N
    ? T[number]
    : PositiveLiteralNumber<N, [...T, `${T["length"]}`]>
  : never;

type RangeLiteralNegative<F extends number, T extends number> = Exclude<
  NegativeLiteralNumbers<F>,
  NegativeLiteralNumbers<T>
>;
type RangeLiteralPositive<F extends number, T extends number> = Exclude<
  PositiveLiteralNumber<T>,
  PositiveLiteralNumber<F>
>;
type RangeLiteral<N1 extends IsInteger<N1>, N2 extends IsInteger<N2>> =
  | (`${N1}` extends `-${string}`
      ? RangeLiteralNegative<N1, 0>
      : `${N1}` extends `${string}`
      ? RangeLiteralPositive<0, N1>
      : never)
  | (`${N2}` extends `-${string}`
      ? RangeLiteralNegative<N2, 0>
      : `${N2}` extends `${string}`
      ? RangeLiteralPositive<0, N2>
      : never);

type RangeLiteralType = RangeLiteral<-5, 3>;
Run Code Online (Sandbox Code Playgroud)


归档时间:

查看次数:

14786 次

最近记录:

7 年 前