programing

숫자를 특정 범위로 제한할 수 있습니까?

lastcode 2023. 2. 13. 20:33
반응형

숫자를 특정 범위로 제한할 수 있습니까?

2.0 RC(RC)는 숫자 리터럴타입할 수 .type t = 1 | 2;256개의 숫자를 쓰지 않고 0~255 등 숫자 범위로 유형을 제한할 수 있습니까?

제 경우 라이브러리에서 팔레트의 색상 값을 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|...

편집: 오래된 답변입니다.TS > = 4.5에는 이 문제를 해결할 수 있는 도구가 제공되었지만, 사용 사례에 따라 제한되거나 제한되지 않을 수도 있습니다.작은 범위의 경우는, 다음의 답이 유효합니다.

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

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

type T = IntRange<20, 300>

----오래된 답변 ----

아니, 가능하지 않아요.그런 종류의 정확한 타입 제약은 타이프스크립트에서는 사용할 수 없습니다(아직은요?

런타임 체크/어세스먼트만이, 다음과 같이 할 수 있습니다.

범위가 작을 경우 항상 다음과 같이 쓸 수 있습니다.

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

let myVar:MyRange = 4; // oops, error :)

물론 정수에 대해서만 작동하며 매우 추악합니다. :)

네, 그럴 도 있어요.UT:

첫 번째.솔루션은 더러운 솔루션이 될 것입니다.두 번째.솔루션은 부분적입니다(x부터 y까지).여기서 y는 작은 숫자이고, 내 경우는 43입니다.세 번째.솔루션은 완전한 솔루션이지만 트랜스포머, 데코레이터 등과 함께 발전합니다.

1. @Adam-Szmyd 솔루션을 사용한 더러운 솔루션(가장 쉽고 빠른 방법 우선):

type RangeType = 1 | 2 | 3

폭넓은 범위를 필요로 하는 경우는, 인쇄와 카피/복사/복사만 하면 됩니다.

// Easiest just incremental
let range = (max) => Array.from(Array(max).keys()).join(" | ");
console.log('Incremental')
console.log(range(20))
// With range and steps
let rangeS = (( min, max, step) => Array.from( new Array( max > min ? Math.ceil((max - min)/step) : Math.ceil((min - max)/step) ), ( x, i ) => max > min ? i*step + min : min - i*step ).join(" | "));
console.log('With range and steps')
console.log(rangeS(3,10,2))

이런 일을 하는 것이 싫을지도 모른다

const data = [1, 2, 4, 5, 6, 7] as const;
type P = typeof data[number];

P가 열거된 유형을 가지고 있음을 나타냅니다.

대신 기능을 사용하여

const rangeType20 = Array.from(Array(20).keys()) as const;

기능으로는 할 수 없는 것을 나타내는

하지만 지금으로선 이게 통하지 않아요 문자 그대로인 경우에만요심지어 오류도 정확하지 않다.

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>;

3.하지만, 는 3으로 됩니다.Transformers,Decorators 등등.

의 함수를 하면, 에서수 있습니다.compiletime이치노 단, 찬찬에서는runtime데레데

현재로서는 불가능하지만 GitHub에 미해결 이슈가 있습니다.현재 제안서를 기다리고 있지만 언젠가 이 기능이 제공될 수 있습니다.

즉, 제안서가 나올 때까지 숫자 범위를 유형으로 사용할 수 없습니다.


갱신 - 2021년8월

제안이 존재합니다.자세한 내용은 구간 유형/부등식 유형을 참조하십시오.

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>

업데이트 1

이므로 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

오리진 답변

타입 스크립트 4.1 재귀 조건 타입을 사용하면 가능합니다.

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'.

그러나 안타깝게도 길이가 너무 길면 재귀 유형이 실패합니다.

type R23 = Range<23>
// TS2589: Type instantiation is excessively deep and possibly infinite.

작동은 하지만 실제로 작동하지는 않습니다.:)

최적의 솔루션은 아니지만(일부 체크는 런타임에 처리되기 때문에) "opaque type"은 예상값을 입력하도록 강제하는 데 도움이 됩니다.

다음은 예를 제시하겠습니다.

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);

유효 범위 번호(양 및 정수 범위)가 있는 경우 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>;

음의 범위이지만 제약이 있습니다.범위는 리터럴입니다.왜 그런지 모르겠지만 나는 리터럴한 마이너스 수치를 얻을 수 없었다.아마 누군가는 알고 있을 거야

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>;

256개의 숫자를 입력하지 않고 예를 들어 0-255와 같은 숫자 범위로 유형을 제한할 수 있습니까?

지금까지는 불가능했지만, 라이프핵을 만들어 코드 한 줄과 복사/붙여넣기 결과로 원하는 시퀀스를 생성할 수 있습니다.

new Array(256).fill(0).map((_, i) => i).join(" | ")

편집: 아, 제공된 답변을 충분히 주의 깊게 읽지 않았습니다.@titusfx는 이미 다른 형식으로 이 답변을 제공했습니다.그의 접근방식과 마찬가지로 이는 생성할 수 있는 숫자의 양에 따라 제한됩니다.이는 실제 솔루션이 아니라 매우 제한된 수의 범위에서 작동하는 회피책입니다.

원답:

여기에는 회피책이 있습니다.답변 https://stackoverflow.com/a/52490977 (이 솔루션에서는 TypeScript v 4.1 이상으로 한정):

type _NumbersFrom0ToN<
    Nr extends number
    > =
    Nr extends Nr ?
        number extends Nr ?
            number :
            Nr extends 0 ?
                never :
                _NumbersFrom0ToNRec<Nr, [], 0> :
        never;

type _NumbersFrom0ToNRec<
    Nr extends number,
    Counter extends any[],
    Accumulator extends number
    > =
    Counter['length'] extends Nr ?
        Accumulator :
        _NumbersFrom0ToNRec<Nr, [any, ...Counter], Accumulator | Counter['length']>;

type NrRange<
    Start extends number,
    End extends number
    > =
    Exclude<_NumbersFrom0ToN<End>, _NumbersFrom0ToN<Start>>;

let nrRange: NrRange<14, 20>;

범위형

해서 '형이 생깁니다.14 | 15 | 16 | 17 | 18 | 19이 작업을 수행하려면 TypeScript가 새로운 개선된 태플 타입 검사 길이 속성을 통해 셀 수 있는 기능을 활용하면 됩니다.따라서 어레이의 길이가 입력 번호와 동일하지 않으면 어레이를 확장합니다.어레이를 확장하는 동안 이미 방문한 길이를 기억합니다.그 결과 카운터가 추가 스텝을 갖게 됩니다.

편집: 이러한 타입을 패키지에 넣어 두면 신뢰성이 높아집니다.https://www.npmjs.com/package/ts-number-range

꼬리 재귀에 대한 나의 해결책

type BuildArray<
    Length extends number, 
    Ele = unknown, 
    Arr extends unknown[] = []
> = Arr['length'] extends Length 
        ? Arr 
        : BuildArray<Length, Ele, [...Arr, Ele]>;

type Add<Num1 extends number, Num2 extends number> =  [...BuildArray<Num1>,...BuildArray<Num2>]['length'];
type Subtract<Num1 extends number, Num2 extends number> = BuildArray<Num1> extends [...arr1: BuildArray<Num2>, ...arr2: infer Rest] ? Rest['length'] : never;

type _RangeOf<start extends number, end extends number, R extends unknown[] = [start]> = R['length'] extends Subtract<end, start> ? [...R, end][number] : _RangeOf<start, end, [...R, Add<start, R['length']> ]> ;

type myRange = _RangeOf<2, 7>; // 2, 3, 4, 5, 6, 7
const myRange: myRange = 7; 
const myRange2: myRange = 1; // error
const myRange2: myRange = 8; // error

여기를 온라인으로 시험해 보세요.

더 좋은 생각이 있어요.

type NumericRange<
    START extends number,
    END extends number,
    ARR extends unknown[] = [],
    ACC extends number = never
> = ARR['length'] extends END
    ? ACC | START | END
    : NumericRange<START, END, [...ARR, 1], ARR[START] extends undefined ? ACC : ACC | ARR['length']>

정적 유형 검사 사용 안 함, 예를 들어 ioT와 같은 라이브러리를 사용할 수 있는 런타임에만 사용taggedUnion예: https://github.com/gcanti/io-ts/issues/313

type IncrementMap = {
  0: 1;
  1: 2;
  2: 3;
  // ...
  1233: 1234;
  1234: 1234; // To make all values also Incrementable
};

type Incrementable = keyof IncrementMap;
type Increment<N extends Incrementable> = IncrementMap[N];

type Range<
  F extends Incrementable,
  T extends Incrementable,
  A extends number[] = [],
> = F extends T ? A[number] | F : Range<Increment<F>, T, [...A, F]>;

let x: Range<1, 10> = 1;
x = 0; // Error
x = 11; // Error

지도 번호 목록은 여기에서 생성할 수 있습니다.

다른 솔루션과 비교했을 때 어떻습니까?분명한 단점은 이 존재한다는 것이다.IncrementMap파일을 파일에 저장함으로써 이 문제를 완화합니다.하지만 독특한 장점은 천장이 없다는 것이다.각 범위는 여전히 999개로 제한되어 있지만 더 큰 범위로 구성할 수 있습니다.

type Foo = Range<0, 998> | Range<999, 1997>;

최대 10k까지 테스트했지만, 유일한 제한은 TypeScript가 객체 내에서 허용하는 속성 양이라고 생각합니다(한계치가 있는지는 확실하지 않지만 유일한 제한은 퍼포먼스일 수 있습니다).또한 10만 등 큰 숫자에도 사용할 수 있습니다(만일의 경우 0부터 끝까지 입력할 필요가 없습니다).물론 음수 또한 효과가 있다.

하지만 문자 그대로의 수학이 있으면 훨씬 더 좋을 텐데.

이것은 HTML 텍스트 영역의 높이를 제한하는 데 도움이 되었습니다.테스트 값이 5 범위까지 클리핑됩니다.20.

const rows = Math.min(Math.max(stringArray.length, 5), 20);

언급URL : https://stackoverflow.com/questions/39494689/is-it-possible-to-restrict-number-to-a-certain-range

반응형