Last active
July 2, 2024 12:37
-
-
Save genki/7a912e6a04c9a01c3efa24d28401965e to your computer and use it in GitHub Desktop.
マスクビット値のUnionからビットマスク型を生成する型関数
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const Flag = { | |
foo: 1, | |
bar: 2, | |
baz: 4, | |
} as const; | |
type MaskType = MaskOf<typeof Flag[keyof typeof Flag]>; | |
// ^? 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | |
type SparseMaskType = MaskOf<1|2|8>; | |
// ^? 0 | 1 | 2 | 3 | 8 | 9 | 10 | 11 | |
type StringToTuple<S extends string> = S extends `${infer Char}${infer Rest}` | |
? [Char, ...StringToTuple<Rest>] | |
: []; | |
// タプルの最初のN個の要素を取り出す型 | |
type Take<T extends any[], N extends number, Acc extends any[] = []> = | |
Acc['length'] extends N | |
? Acc | |
: T extends [infer Head, ...infer Tail] | |
? Take<Tail, N, [...Acc, Head]> | |
: Acc; | |
// タプルを文字列に変換するヘルパー型 | |
type TupleToString<T extends any[]> = T extends [infer Char, ...infer Rest] | |
? `${Char & string}${TupleToString<Rest>}` | |
: ''; | |
// 文字列型からN文字取り出す型関数 | |
type TakeString<S extends string, N extends number> = | |
TupleToString<Take<StringToTuple<S>, N>>; | |
export type ParseNum<T extends string> | |
= T extends `${infer Int extends number}` ? Int : never; | |
type ReverseString<S extends string> = S extends `${infer First}${infer Rest}` | |
? `${ReverseString<Rest>}${First}` | |
: ""; | |
type RemoveLeadingZeros<S extends string, R extends string = ""> = S extends "0" | |
? S | |
: S extends `0{infer R}` | |
? RemoveLeadingZeros<R> : S; | |
type PutSign<S extends string> = `-${S}`; | |
type InternalMinusOne<S extends string> = | |
S extends `${infer Digit extends number}${infer Rest}` | |
? Digit extends 0 | |
? `9${InternalMinusOne<Rest>}` | |
: `${[9, 0, 1, 2, 3, 4, 5, 6, 7, 8][Digit]}${Rest}` | |
: never; | |
type InternalPlusOne<S extends string> = S extends "9" | |
? "01" | |
: S extends `${infer Digit extends number}${infer Rest}` | |
? Digit extends 9 | |
? `0${InternalPlusOne<Rest>}` | |
: `${[1, 2, 3, 4, 5, 6, 7, 8, 9][Digit]}${Rest}` | |
: never; | |
export type MinusOne<T extends number> = T extends 0 | |
? -1 // T = 0 | |
: `${T}` extends `-${infer Abs}` | |
? ParseNum<PutSign<ReverseString<InternalPlusOne<ReverseString<Abs>>>>> | |
// T < 0 | |
// T > 0 | |
: ParseNum< | |
RemoveLeadingZeros<ReverseString<InternalMinusOne<ReverseString<`${T}`>>>> | |
>; | |
export type PlusOne<T extends number> = T extends -1 | |
? 0 | |
: `${T}` extends `-${infer Abs}` | |
? ParseNum< | |
PutSign< | |
RemoveLeadingZeros<ReverseString<InternalMinusOne<ReverseString<Abs>>>> | |
> | |
> | |
: ParseNum< | |
RemoveLeadingZeros<ReverseString<InternalPlusOne<ReverseString<`${T}`>>>> | |
>; | |
type Create10ToPower<Power extends number> | |
= ParseNum< `1${Repeat<"0", Power>}`>; | |
type Repeat< | |
T extends string, | |
n extends number, | |
acc extends string = "" | |
> = n extends 0 ? acc : Repeat<T, MinusOne<n>, `${acc}${T}`>; | |
type AddBiggerPowerOf10ToN< | |
PowerOf10 extends string, | |
N extends string | |
> = `${ReverseString<Drop<ReverseString<PowerOf10>, LengthOfString<N>>>}${N}`; | |
export type LengthOfString< | |
S extends string, | |
len extends number = 0 | |
> = S extends `${infer _}${infer rest}` | |
? LengthOfString<rest, PlusOne<len>> | |
: len; | |
type Drop<S extends string, N extends number> = N extends 0 | |
? S | |
: S extends `${infer _}${infer Rest}` | |
? Drop<Rest, MinusOne<N>> | |
: S; | |
type AddSmallerPowerOf10ToN<Power extends number, N extends number> = ParseNum< | |
ReverseString< `${TakeString<ReverseString<`${N}`>, Power>}${InternalPlusOne< | |
Drop<ReverseString<`${N}`>, Power> | |
>}`> | |
>; | |
type isShorter< | |
A extends string, | |
B extends String | |
> = A extends `${infer _}${infer Rest}` | |
? B extends `${infer _}${infer Rest2}` | |
? isShorter<Rest, Rest2> | |
: false | |
: B extends `${infer _}${infer _}` | |
? true | |
: false; | |
type GTTable = [ | |
["=", "<", "<", "<", "<", "<", "<", "<", "<", "<"], | |
[">", "=", "<", "<", "<", "<", "<", "<", "<", "<"], | |
[">", ">", "=", "<", "<", "<", "<", "<", "<", "<"], | |
[">", ">", ">", "=", "<", "<", "<", "<", "<", "<"], | |
[">", ">", ">", ">", "=", "<", "<", "<", "<", "<"], | |
[">", ">", ">", ">", ">", "=", "<", "<", "<", "<"], | |
[">", ">", ">", ">", ">", ">", "=", "<", "<", "<"], | |
[">", ">", ">", ">", ">", ">", ">", "=", "<", "<"], | |
[">", ">", ">", ">", ">", ">", ">", ">", "=", "<"], | |
[">", ">", ">", ">", ">", ">", ">", ">", ">", "="] | |
]; | |
type GTSameLength< | |
A extends string, | |
B extends string | |
> = A extends `${infer DigitA extends number}${infer RestA}` | |
? B extends `${infer DigitB extends number}${infer RestB}` | |
? GTTable[DigitA][DigitB] extends ">" | |
? true | |
: GTTable[DigitA][DigitB] extends "<" | |
? false | |
: GTSameLength<RestA, RestB> | |
: false | |
: false; | |
export type Greater<A extends number, B extends number> = InternalGT< | |
`${A}`, | |
`${B}` | |
>; | |
type InternalGT<A extends string, B extends string> = isShorter< | |
A, | |
B | |
> extends true | |
? false | |
: isShorter<B, A> extends true | |
? true | |
: GTSameLength<A, B>; | |
type Plus10ToPower<N extends number, Power extends number> = Power extends 0 | |
? PlusOne<N> | |
: Greater<Create10ToPower<Power>, N> extends true | |
? ParseNum<AddBiggerPowerOf10ToN< `${Create10ToPower<Power>}`, `${N}`>> | |
: AddSmallerPowerOf10ToN<Power, N>; | |
type AddPowerOf10NTimes< | |
sum extends number, | |
power extends number, | |
N extends number | |
> = N extends 0 | |
? sum | |
: AddPowerOf10NTimes<Plus10ToPower<sum, power>, power, MinusOne<N>>; | |
type Adder< | |
sum extends number, | |
reversedDigits, | |
currentPower extends number = 0 | |
> = reversedDigits extends `${infer d extends number}${infer rest}` | |
? Adder<AddPowerOf10NTimes<sum, currentPower, d>, rest, PlusOne<currentPower>> | |
: sum; | |
export type SimpleAdd<A extends number, B extends number> | |
= Adder<A, ReverseString<`${B}`>>; | |
export type SimpleSum<A extends number[]> = | |
A extends [infer Head extends number, ...infer Rest extends number[]] | |
? SimpleAdd<Head, SimpleSum<Rest>> | |
: 0; | |
type UnionToIntersection<U> = | |
(U extends unknown ? (x: U) => void : never) extends (x: infer I) => void ? I : never; | |
type LastOf<U> = | |
UnionToIntersection<U extends unknown ? () => U : never> extends () => infer R ? R : never; | |
export type UnionToTuple<T, L = LastOf<T>> = | |
[T] extends [never] ? [] : [...UnionToTuple<Exclude<T, L>>, L]; | |
export type Range<N extends number, R extends number[] = []> = | |
R['length'] extends N ? R[number] : Range<N, [...R, PlusOne<R['length']>]>; | |
type MaskOfA<A extends number[]> = A extends | |
[infer H extends number, ...infer R extends number[]] | |
? R extends [] | |
? 0|H | |
: H|MaskOfA<R>|SimpleAdd<H,MaskOfA<R>> | |
: never; | |
export type MaskOf<T extends number> = UnionToTuple<T> extends number[] | |
? MaskOfA<UnionToTuple<T>> | |
: never; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
cf. https://softwaremill.com/implementing-advanced-type-level-arithmetic-in-typescript-part-1/