Skip to content

Instantly share code, notes, and snippets.

@genki
Last active July 2, 2024 12:37
Show Gist options
  • Save genki/7a912e6a04c9a01c3efa24d28401965e to your computer and use it in GitHub Desktop.
Save genki/7a912e6a04c9a01c3efa24d28401965e to your computer and use it in GitHub Desktop.
マスクビット値のUnionからビットマスク型を生成する型関数
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;
@genki
Copy link
Author

genki commented Jul 2, 2024

Playground
https://www.typescriptlang.org/play/?#code/MYewdgzgLgBAYgGwIYHMYF4YG8BQMYBmIIAXDAIwA0eMARkgE5kBM1+9AXmQCzUC+MJBBihIUANw4cUAJ4AHAKYwAskIDWAFXlLMqiGoDyBADyzFIAvGQoA2moUyLMMwqeJUAXQB8kgPS-8GAA9AH4pFxgAZTlGCAU9TW0MFXUjY3IAH2YMgA4fHH9A0PCkyKgGAEswFA0QDQBXOQQFY0iYBQAPKAUwABNhaErqr2S2zu6+4QADABIsKoIFBhgAYQALRj45haWYACUFaD4pmhCYG3XGShgAOjuyoZq6xubjA+gvDxoyGw9JAoCgH6GQDrDIBrhkAdgyAAHNALhK4IAcoBoBXBgEDIwAEvoAkhkAa8qAKIZAF+KgE0GQDR6tIkhokPZjBp2l0ev1BGAZL9rnDqRM6WB6gBbWhLa4AQWAwFZtOESAZv2SvxG6BoAuANgA5M1qlA1gqPMLJjA4TR8Gc5bqYGQqeMRecdssABIKJC9a53G4WmBkioIL6BQJnMkUl0IZnXGwOuXXa227yG-BkOX-Qog0HowDhpoB1bUA6EqAawZAJCagG3jfGAaIZABsMoMAiwyAH4ZiREGk0FLUHlUUJTNXSxYzvMkTTStTYnZcGPa7k73lB3TAzrMsD2YAAyGCDOtbLCV5o18p1t6HKBeY7fGAKhUxgLJlOEwDSDIBIhjhyZxBMJgCLUwAOpiTFM7yQpa9VWo2BivqszPzAOdySzSjQi7ViAb71t6LQQbUoGtF4zJePkOCdHIIAMLAEQAAqxAocJcg2pparOww0Jg7ZstM2xgIsywAJJgLARHslyPIMMcI4wAxsBkGACgAG5LJIEQHIJDBxBBH7MV+jzSlEf5jk6cAVOJUDzgO65bnqMBjqJSwSd+9aDpuczKapWlGjAABEVnCUkByciAgkADI2r0dYAFpLCAEBSR2dIkSg1x7H+gXJDZcljP5whWQADFZpxRNuUWUTpsXzDRux7BZHpnA5TkKK5tqed5vl7CMZCRHZT5YfUUCRBUKBgH5qWBXJUwALRzJExzVUo3FLGASAIMoVT1BABh8S1ZptRgNApWaimZcsAAijUVEx0X-qxSzqct+yaScuUwGtKAbX+sURpxUwAJxzANDBDSNY0TVNxk5YEZBjjYN3XLF1xUDArAwAAzNcvAwAArNcABs1wAOzXDkHg2KdG0ePOg4fbxAlCY+-WMYNw1YQg42TdBoWGZFf5WTdCXaXF5D05ZC1akttEnetm2pQBbF7RzWNHZxaPc2aN2GqOsX3YTj3E6Tr0tO9QuRjpcw2IDwNgzAEPQzAcMwIjMA5NcN0oyLGNzIL258WJkioehmFJKNHIK4RW280BbYXYlHXkDAhRUpgl0q2OGgcdJOldRlHN8rQEAWWcOHiXhBG1fVjXNXpyeSQ9T0k2Tb249nhnGLHEBIRXNABzAxgwMH-sBFSIz12QSdxPhnLGFd+UuW5JUMD5a5iQZjzGLnw3OwXitFyPq6h8cFdeIa+T2xhzhJPnrsUWaHsMHJ29ar7iUt6rC7h1tnXUTHccJzAbcp53V1pw1TVdx6Ho94VffVF5A9lTPr4S7j2ei7cmQ99KANHmXReV0l4ejgZZe+Hc37v0-kVdyP9SrgOLqPYBm8wFZ1nu+eeUwYHwL6qsBgNpujkFirULCIAADuSxjAMOYcsCOu8EH4EwEggiOlyCWwUIoJAUBjBxSstcNhQEF4UIOCIsRIFKaPDYP+P8u9VFIEFMous4UEpyTABdTiWihRkHkdQyk1xJ6uzAAhU+Jj5xh1IRQvkvRegACFGooCWNIhgRhaG1DhCg3x-jYo6J-DQFkEdAo4HakI4ekDVwrQHnIbBRD6whIILQuxrkVRrCMJJOEFd5xwl6ihDoaE14RFySgVUBSS7zXCUFGgyp1E7WWEHWJowFJX12AAfX5rsKhRwhZnBqXUggklhlQCkfLMByokLbmVP8CIySQCpNZgFQyv5OHtLklEra9cziRGSj06O-TBnLCttpNZqTBxWJemAopCDKorKSK43okROTDWaAwTJASQBBN8W0wCvZtQgrYnJPhj98CEMSe+U+UFJJwskmOUppCpFMJkdLbossED4KmoaW5aT4X1jRQvTF7CEGblIbEihFQICRDWA7FhsommqPcX+CCXTMB8jOU6AZvSrmHUSpyiO7MLlCoOtAZgH0zgMqZSyhga5oDBXXMwbhlkCDDTiNuMVF8pWCvOcsAZoznAMHqAobc2qEBxAoQAcQ0GSWgzRJQ0BsFZdAkjrLiOuFZX1PrvX+qDQG4NfrQ0Rqsh4VRHqvBBq9eGkNSbE0psDam4N0b3VWTjX6nN1kE1psLWGotkbE2ZvwLGoNebs3xuTSWutxaM0xprbmqtba-UFsbaW+t5bzgtustWwd7b80Nu7U2rNQ7W1ToHcOz1o6k29srdO-tK7J0zo7fOxdq7Z1ru3cuzt4it27uPTu09G6vh-HxjAR1kQkCci-nklBfLonbJoPq1qhkeUwGfQa41nMzpQB-TzdplzpWAdvu+xaUqRaQa1LvUDg53FyuvU6pALqFCoy5nyM2XN3EagjjWq6ZxyiWqumQR1zrmiYYA9h6jG08M03EURwgOqrXv0sjeu9D7alrBVYBtV0B3GarIDa3VKtRMKH+KvR2T57VUNESwoDO92nXFgyxUFclgGOpQWOPkxxVFjiQycZCEQtMaFLuymAamZJ1jkgq5lGFWX4D5ByrpEcSNsc4hJ7c9mlXGHcfyEY7mLWeeIyF7cnH73jN4y5qzJmN7ywBb44wBzgOgspbsXZGnkjAojkcu+cyppFIi-J3FxgVilYUElrFyrfF2KKX+DziVoWlzcZ4lA3i-k1dCYE2uY4KvUKq3QkAyW6v6dPuixZKsPlfJ+T47rWThtApq4hN5T4Pn-NinCDQFR72+RoBALkELeQ0DQuw47vZIkXa-als0+XDucm3BthbtDtu7cOKwxLw3ksPeuGdoCf2VsqEeUVpCa2lAfKczOI7WW2KqKoQk3oIsICqOAPUBgVDGK5fdu05Il05II4gUjrmwhxVSt6Bd0D0zb6Q+Vc99hoS3t7eML9kQ6PMdQF8dcXodjpmzKnsYNHGOeic5q1NyyD2pMVIduvJ8DVORVg+RZ2HvIrMXbgbytxim1UJNRXMIzYOpDSdl0oeXVZIgESU3B9pUo5rOb-F2faoYKcq7BQ6DS0ALu-GHNpM3zQlfO+uH718BFjLCbruDmAABVMAFRwC1AenEYAUA4-NSj8BfAAAKKPf56hgDUGAJhhiziZ46GQKPABKDAIx+IgAqBTnGYkq8R1L2QJ0dEq-oBr3XinZw6KWRtnjK9zkhBQDSOnu30fY-x5AInhQyfU-GBzxHPPBei+cUz53kYOfG+exb1vmAA5OIhV3wwKXlSZNKBj6n2CLwWgaGuM5ZII-oBpA0EhSfNgND4a2jYQfDANQzgJQfgHRr8Z84IABRDoYAUmXoe-R-JCR-S9cpC-E3fYMUbxFLC7YKL3DUTAW3GUWFRUZUHjdUP8FkPKP-dpDUMxDAloOEAMB0PYfnV2PYYgnoUg7wbwSPBIIwPkZXHHUFAg79T8d1J0S0bA24fsfaEKV3b3CWfYB3H3T0OuDIS0MjGAS0DIXgggfg8qDIYPAPSgHQvQ8XFWf-c-GXCIHQt2NLSFZIMAsAW-KsSkILQQtiX4RKEwpfafJw54Fw9-F5f8IucQIAA

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment