typescript - Birleşim tipini nasıl filtrelerim?
Aşağıdaki gibi bir birlik (union) türüm var:
type Animal = "cat" | "dog" | "rabbit"
Ve bu hayvanlar arasındaki tüm kombinasyonların birleşim türüne sahibim:
type PairsMap = {
[K1 in Animal]: {
[K2 in Animal]: K1 extends K2 ? never : [K1, K2]
}
}
type Values<T> = T[keyof T]
type Pairs = Values<Values<PairsMap>>
// type Pairs = ["dog", "cat"] | ["rabbit", "cat"] | ["cat", "dog"] | ["rabbit", "dog"] | ["cat", "rabbit"] | ["dog", "rabbit"]
Soru şu - "tersi alınmış" çiftlerin tümünü nasıl kaldırabilirim? (örneğin, cat-dog varsa dog-cat'i kaldırmak).
TS Playground bağlantısı
Cevap #1
Maalesef basit bir çözüm bulamadım. Eğer bilen varsa daha basit bir çözüm - dinlemeye hazırım.
PairsMap uygulamasını yeniden yazdım. Daha genel olduğunu düşünüyorum ama yanlış olabilirim, bu yüzden istediğiniz herhangi bir uygulamayı kullanabilirsiniz. Herkesin kendi tarzı vardır, değil mi ?:D
// credits goes to https://github.com/microsoft/TypeScript/issues/31751#issuecomment-498526919
type IsNever<T> = [T] extends [never] ? true : false;
type PairsMap<
Union extends string,
Used extends string = never,
Cache extends string[] = never
> =
IsNever<Union> extends true ? Cache : {
[Prop in Union]: PairsMap<Exclude<Union, Prop>, Prop, [Prop, Used]>
}[Union]
type PartialPermutations = PairsMap<Animal>
Sonraki adımda, birleşimi filtrelememiz gerekiyor. Birleşim üzerinde yineleme yapamayacağımız için onu tuple'a dönüştürmemiz gerekiyor.
// credits goes to https://stackoverflow.com/a/50375286
type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (
k: infer I
) => void
? I
: never;
// credits goes to https://github.com/microsoft/TypeScript/issues/13298#issuecomment-468114901
type UnionToOvlds<U> = UnionToIntersection<
U extends any ? (f: U) => void : never
>;
// credits goes to https://github.com/microsoft/TypeScript/issues/13298#issuecomment-468114901
type PopUnion<U> = UnionToOvlds<U> extends (a: infer A) => void ? A : never;
// credit goes to https://stackoverflow.com/questions/53953814/typescript-check-if-a-type-is-a-union#comment-94748994
type IsUnion<T> = [T] extends [UnionToIntersection<T>] ? false : true;
type UnionToArray<T, A extends unknown[] = []> = IsUnion<T> extends true
? UnionToArray<Exclude<T, PopUnion<T>>, [PopUnion<T>, ...A]>
: [T, ...A];
Benzer soru
Dikkat etmiş olabileceğiniz gibi, her tür yardımcı elemanın üzerinde açıklama ile birlikte bağlantılar vardır.
Makale
Bunu hatasız veri yapısına (tuple) dönüştürdükten sonra, aslında üzerinde yineleme yapabiliriz:
type Inverse<T> = T extends [infer Fst, infer Scd] ? [Scd, Fst] : never
type MapPredicate<Original, Tuple> = Inverse<Tuple> extends Original ? never : Tuple
type Filter<
Current extends Array<any>,
Result extends Array<any> = []
> = Current extends []
? []
: Current extends [infer H]
? [...Result, MapPredicate<Result[number], H>][number]
: Current extends [infer Head, ...infer Tail]
? Filter<[...Tail], [...Result, MapPredicate<Result[number], Head>]>
: Readonly<Result>;
Burada, blogumda, bunun nasıl çalıştığına dair daha fazla açıklama bulabilirsiniz
İşte burada neler olup bittiğini anlamanız için saf js var. Lütfen null değerini never olarak ele alın. Dediğim gibi - bu bir sözdizimi örneği, TypeScript'te never değerini null olarak ele almamalısınız
const predicate = (result, head) => {
const [fst, scd] = head
// pseudocode, includes works with primitives
if (result.includes([scd, fst])) {
return null
}
return head
}
const filter = (current, result = []) => {
if (current.length === 0) {
return []
}
const [head, ...tail] = current;
return filter(tail, [...result, predicate(result, head)])
}
Tam örnek:
type Animal = "cat" | "dog" | "rabbit"
// credits goes to https://stackoverflow.com/a/50375286
type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (
k: infer I
) => void
? I
: never;
// credits goes to https://github.com/microsoft/TypeScript/issues/13298#issuecomment-468114901
type UnionToOvlds<U> = UnionToIntersection<
U extends any ? (f: U) => void : never
>;
// credits goes to https://github.com/microsoft/TypeScript/issues/13298#issuecomment-468114901
type PopUnion<U> = UnionToOvlds<U> extends (a: infer A) => void ? A : never;
// credit goes to https://stackoverflow.com/questions/53953814/typescript-check-if-a-type-is-a-union#comment-94748994
type IsUnion<T> = [T] extends [UnionToIntersection<T>] ? false : true;
type UnionToArray<T, A extends unknown[] = []> = IsUnion<T> extends true
? UnionToArray<Exclude<T, PopUnion<T>>, [PopUnion<T>, ...A]>
: [T, ...A];
type IsNever<T> = [T] extends [never] ? true : false;
type PairsMap<
Union extends string,
Used extends string = never,
Cache extends string[] = never
> =
IsNever<Union> extends true ? Cache : {
[Prop in Union]: PairsMap<Exclude<Union, Prop>, Prop, [Prop, Used]>
}[Union]
type PartialPermutations = PairsMap<Animal>
type Inverse<T> = T extends [infer Fst, infer Scd] ? [Scd, Fst] : never
type MapPredicate<Original, Tuple> = Inverse<Tuple> extends Original ? never : Tuple
type Filter<
Current extends Array<any>,
Result extends Array<any> = []
> = Current extends []
? []
: Current extends [infer H]
? [...Result, MapPredicate<Result[number], H>][number]
: Current extends [infer Head, ...infer Tail]
? Filter<[...Tail], [...Result, MapPredicate<Result[number], Head>]>
: Readonly<Result>;
type Result = Filter<UnionToArray<PartialPermutations>>; // ["dog", "cat"] | ["rabbit", "dog"] | ["rabbit", "cat"]
Oyun alanı
